| ... | @@ -35,24 +35,24 @@ const ModuleInfo = struct { | ... | @@ -35,24 +35,24 @@ const ModuleInfo = struct { |
| 35 | /// The type that this entity represents. This is just | 35 | /// The type that this entity represents. This is just |
| 36 | /// the instruction opcode. | 36 | /// the instruction opcode. |
| 37 | kind: Opcode, | 37 | kind: Opcode, |
| 38 | /// Offset of first child result-id, stored in entity_children. | 38 | /// The offset of this entity's operands, in |
| 39 | /// These are the shallow entities appearing directly in the | 39 | /// `binary.instructions`. |
| 40 | /// type's instruction. | 40 | first_operand: u32, |
| 41 | first_child: u32, | 41 | /// The number of operands in this entity |
| 42 | /// Offset to the first word of extra-data: Data in the instruction | 42 | num_operands: u16, |
| 43 | /// that must be considered for uniqueness, but doesn't include | 43 | /// The (first_operand-relative) offset of the result-id, |
| 44 | /// any IDs. | 44 | /// or the entity that is affected by this entity if this entity |
| 45 | first_extra_data: u32, | 45 | /// is a decoration. |
| | 46 | result_id_index: u16, |
| 46 | }; | 47 | }; |
| 47 | | 48 | |
| 48 | /// Maps result-id to Entity's | 49 | /// Maps result-id to Entity's |
| 49 | entities: std.AutoArrayHashMapUnmanaged(ResultId, Entity), | 50 | entities: std.AutoArrayHashMapUnmanaged(ResultId, Entity), |
| 50 | /// The list of children per instruction. | 51 | /// A bit set that keeps track of which operands are result-ids. |
| 51 | entity_children: []const ResultId, | 52 | /// Note: This also includes any result-id! |
| 52 | /// The list of extra data per instruction. | 53 | /// Because we need these values when recoding the module anyway, |
| 53 | /// TODO: This is a bit awkward, maybe we need to store it some | 54 | /// it contains the status of ALL operands in the module. |
| 54 | /// other way? | 55 | operand_is_id: std.DynamicBitSetUnmanaged, |
| 55 | extra_data: []const u32, | | |
| 56 | | 56 | |
| 57 | pub fn parse( | 57 | pub fn parse( |
| 58 | arena: Allocator, | 58 | arena: Allocator, |
| ... | @@ -60,19 +60,22 @@ const ModuleInfo = struct { | ... | @@ -60,19 +60,22 @@ const ModuleInfo = struct { |
| 60 | binary: BinaryModule, | 60 | binary: BinaryModule, |
| 61 | ) !ModuleInfo { | 61 | ) !ModuleInfo { |
| 62 | var entities = std.AutoArrayHashMap(ResultId, Entity).init(arena); | 62 | var entities = std.AutoArrayHashMap(ResultId, Entity).init(arena); |
| 63 | var entity_children = std.ArrayList(ResultId).init(arena); | | |
| 64 | var extra_data = std.ArrayList(u32).init(arena); | | |
| 65 | var id_offsets = std.ArrayList(u16).init(arena); | 63 | var id_offsets = std.ArrayList(u16).init(arena); |
| | 64 | var operand_is_id = try std.DynamicBitSetUnmanaged.initEmpty(arena, binary.instructions.len); |
| 66 | | 65 | |
| 67 | var it = binary.iterateInstructions(); | 66 | var it = binary.iterateInstructions(); |
| 68 | while (it.next()) |inst| { | 67 | while (it.next()) |inst| { |
| 69 | if (inst.opcode == .OpFunction) break; // No more declarations are possible | | |
| 70 | if (!canDeduplicate(inst.opcode)) continue; | | |
| 71 | | | |
| 72 | id_offsets.items.len = 0; | 68 | id_offsets.items.len = 0; |
| 73 | try parser.parseInstructionResultIds(binary, inst, &id_offsets); | 69 | try parser.parseInstructionResultIds(binary, inst, &id_offsets); |
| 74 | | 70 | |
| 75 | const result_id_index: u32 = switch (inst.opcode.class()) { | 71 | const first_operand_offset: u32 = @intCast(inst.offset + 1); |
| | 72 | for (id_offsets.items) |offset| { |
| | 73 | operand_is_id.set(first_operand_offset + offset); |
| | 74 | } |
| | 75 | |
| | 76 | if (!canDeduplicate(inst.opcode)) continue; |
| | 77 | |
| | 78 | const result_id_index: u16 = switch (inst.opcode.class()) { |
| 76 | .TypeDeclaration, .Annotation, .Debug => 0, | 79 | .TypeDeclaration, .Annotation, .Debug => 0, |
| 77 | .ConstantCreation => 1, | 80 | .ConstantCreation => 1, |
| 78 | else => unreachable, | 81 | else => unreachable, |
| ... | @@ -80,27 +83,6 @@ const ModuleInfo = struct { | ... | @@ -80,27 +83,6 @@ const ModuleInfo = struct { |
| 80 | | 83 | |
| 81 | const result_id: ResultId = @enumFromInt(inst.operands[id_offsets.items[result_id_index]]); | 84 | const result_id: ResultId = @enumFromInt(inst.operands[id_offsets.items[result_id_index]]); |
| 82 | | 85 | |
| 83 | const first_child: u32 = @intCast(entity_children.items.len); | | |
| 84 | const first_extra_data: u32 = @intCast(extra_data.items.len); | | |
| 85 | | | |
| 86 | try entity_children.ensureUnusedCapacity(id_offsets.items.len - 1); | | |
| 87 | try extra_data.ensureUnusedCapacity(inst.operands.len - id_offsets.items.len); | | |
| 88 | | | |
| 89 | var id_i: usize = 0; | | |
| 90 | for (inst.operands, 0..) |operand, i| { | | |
| 91 | assert(id_i == id_offsets.items.len or id_offsets.items[id_i] >= i); | | |
| 92 | if (id_i != id_offsets.items.len and id_offsets.items[id_i] == i) { | | |
| 93 | // Skip .IdResult / .IdResultType. | | |
| 94 | if (id_i != result_id_index) { | | |
| 95 | entity_children.appendAssumeCapacity(@enumFromInt(operand)); | | |
| 96 | } | | |
| 97 | id_i += 1; | | |
| 98 | } else { | | |
| 99 | // Non-id operand, add it to extra data. | | |
| 100 | extra_data.appendAssumeCapacity(operand); | | |
| 101 | } | | |
| 102 | } | | |
| 103 | | | |
| 104 | switch (inst.opcode.class()) { | 86 | switch (inst.opcode.class()) { |
| 105 | .Annotation, .Debug => { | 87 | .Annotation, .Debug => { |
| 106 | // TODO | 88 | // TODO |
| ... | @@ -113,8 +95,9 @@ const ModuleInfo = struct { | ... | @@ -113,8 +95,9 @@ const ModuleInfo = struct { |
| 113 | } | 95 | } |
| 114 | entry.value_ptr.* = .{ | 96 | entry.value_ptr.* = .{ |
| 115 | .kind = inst.opcode, | 97 | .kind = inst.opcode, |
| 116 | .first_child = first_child, | 98 | .first_operand = first_operand_offset, |
| 117 | .first_extra_data = first_extra_data, | 99 | .num_operands = @intCast(inst.operands.len), |
| | 100 | .result_id_index = result_id_index, |
| 118 | }; | 101 | }; |
| 119 | }, | 102 | }, |
| 120 | else => unreachable, | 103 | else => unreachable, |
| ... | @@ -123,34 +106,9 @@ const ModuleInfo = struct { | ... | @@ -123,34 +106,9 @@ const ModuleInfo = struct { |
| 123 | | 106 | |
| 124 | return ModuleInfo{ | 107 | return ModuleInfo{ |
| 125 | .entities = entities.unmanaged, | 108 | .entities = entities.unmanaged, |
| 126 | .entity_children = entity_children.items, | 109 | .operand_is_id = operand_is_id, |
| 127 | .extra_data = extra_data.items, | | |
| 128 | }; | 110 | }; |
| 129 | } | 111 | } |
| 130 | | | |
| 131 | /// Fetch a slice of children for the index corresponding to an entity. | | |
| 132 | fn childrenByIndex(self: ModuleInfo, index: usize) []const ResultId { | | |
| 133 | const values = self.entities.values(); | | |
| 134 | const first_child = values[index].first_child; | | |
| 135 | if (index == values.len - 1) { | | |
| 136 | return self.entity_children[first_child..]; | | |
| 137 | } else { | | |
| 138 | const next_first_child = values[index + 1].first_child; | | |
| 139 | return self.entity_children[first_child..next_first_child]; | | |
| 140 | } | | |
| 141 | } | | |
| 142 | | | |
| 143 | /// Fetch the slice of extra-data for the index corresponding to an entity. | | |
| 144 | fn extraDataByIndex(self: ModuleInfo, index: usize) []const u32 { | | |
| 145 | const values = self.entities.values(); | | |
| 146 | const first_extra_data = values[index].first_extra_data; | | |
| 147 | if (index == values.len - 1) { | | |
| 148 | return self.extra_data[first_extra_data..]; | | |
| 149 | } else { | | |
| 150 | const next_extra_data = values[index + 1].first_extra_data; | | |
| 151 | return self.extra_data[first_extra_data..next_extra_data]; | | |
| 152 | } | | |
| 153 | } | | |
| 154 | }; | 112 | }; |
| 155 | | 113 | |
| 156 | const EntityContext = struct { | 114 | const EntityContext = struct { |
| ... | @@ -158,13 +116,7 @@ const EntityContext = struct { | ... | @@ -158,13 +116,7 @@ const EntityContext = struct { |
| 158 | ptr_map_a: std.AutoArrayHashMapUnmanaged(ResultId, void) = .{}, | 116 | ptr_map_a: std.AutoArrayHashMapUnmanaged(ResultId, void) = .{}, |
| 159 | ptr_map_b: std.AutoArrayHashMapUnmanaged(ResultId, void) = .{}, | 117 | ptr_map_b: std.AutoArrayHashMapUnmanaged(ResultId, void) = .{}, |
| 160 | info: *const ModuleInfo, | 118 | info: *const ModuleInfo, |
| 161 | | 119 | binary: *const BinaryModule, |
| 162 | fn init(a: Allocator, info: *const ModuleInfo) EntityContext { | | |
| 163 | return .{ | | |
| 164 | .a = a, | | |
| 165 | .info = info, | | |
| 166 | }; | | |
| 167 | } | | |
| 168 | | 120 | |
| 169 | fn deinit(self: *EntityContext) void { | 121 | fn deinit(self: *EntityContext) void { |
| 170 | self.ptr_map_a.deinit(self.a); | 122 | self.ptr_map_a.deinit(self.a); |
| ... | @@ -203,14 +155,19 @@ const EntityContext = struct { | ... | @@ -203,14 +155,19 @@ const EntityContext = struct { |
| 203 | } | 155 | } |
| 204 | } | 156 | } |
| 205 | | 157 | |
| 206 | // Hash extra data | 158 | // Process operands |
| 207 | for (self.info.extraDataByIndex(index)) |data| { | 159 | const operands = self.binary.instructions[entity.first_operand..][0..entity.num_operands]; |
| 208 | std.hash.autoHash(hasher, data); | 160 | for (operands, 0..) |operand, i| { |
| 209 | } | 161 | if (i == entity.result_id_index) { |
| 210 | | 162 | // Not relevant, skip... |
| 211 | // Hash children | 163 | continue; |
| 212 | for (self.info.childrenByIndex(index)) |child| { | 164 | } else if (self.info.operand_is_id.isSet(entity.first_operand + i)) { |
| 213 | try self.hashInner(hasher, child); | 165 | // Operand is ID |
| | 166 | try self.hashInner(hasher, @enumFromInt(operand)); |
| | 167 | } else { |
| | 168 | // Operand is merely data |
| | 169 | std.hash.autoHash(hasher, operand); |
| | 170 | } |
| 214 | } | 171 | } |
| 215 | } | 172 | } |
| 216 | | 173 | |
| ... | @@ -228,7 +185,11 @@ const EntityContext = struct { | ... | @@ -228,7 +185,11 @@ const EntityContext = struct { |
| 228 | const entity_a = self.info.entities.values()[index_a]; | 185 | const entity_a = self.info.entities.values()[index_a]; |
| 229 | const entity_b = self.info.entities.values()[index_b]; | 186 | const entity_b = self.info.entities.values()[index_b]; |
| 230 | | 187 | |
| 231 | if (entity_a.kind != entity_b.kind) return false; | 188 | if (entity_a.kind != entity_b.kind) { |
| | 189 | return false; |
| | 190 | } else if (entity_a.result_id_index != entity_a.result_id_index) { |
| | 191 | return false; |
| | 192 | } |
| 232 | | 193 | |
| 233 | if (entity_a.kind == .OpTypePointer) { | 194 | if (entity_a.kind == .OpTypePointer) { |
| 234 | // May be a forward reference, or should be saved as a potential | 195 | // May be a forward reference, or should be saved as a potential |
| ... | @@ -246,18 +207,28 @@ const EntityContext = struct { | ... | @@ -246,18 +207,28 @@ const EntityContext = struct { |
| 246 | } | 207 | } |
| 247 | } | 208 | } |
| 248 | | 209 | |
| 249 | // Check if extra data is the same. | 210 | const operands_a = self.binary.instructions[entity_a.first_operand..][0..entity_a.num_operands]; |
| 250 | if (!std.mem.eql(u32, self.info.extraDataByIndex(index_a), self.info.extraDataByIndex(index_b))) { | 211 | const operands_b = self.binary.instructions[entity_b.first_operand..][0..entity_b.num_operands]; |
| | 212 | |
| | 213 | // Note: returns false for operands that have explicit defaults in optional operands... oh well |
| | 214 | if (operands_a.len != operands_b.len) { |
| 251 | return false; | 215 | return false; |
| 252 | } | 216 | } |
| 253 | | 217 | |
| 254 | // Recursively check if children are the same | 218 | for (operands_a, operands_b, 0..) |operand_a, operand_b, i| { |
| 255 | const children_a = self.info.childrenByIndex(index_a); | 219 | const a_is_id = self.info.operand_is_id.isSet(entity_a.first_operand + i); |
| 256 | const children_b = self.info.childrenByIndex(index_b); | 220 | const b_is_id = self.info.operand_is_id.isSet(entity_b.first_operand + i); |
| 257 | if (children_a.len != children_b.len) return false; | 221 | if (a_is_id != b_is_id) { |
| 258 | | 222 | return false; |
| 259 | for (children_a, children_b) |child_a, child_b| { | 223 | } else if (i == entity_a.result_id_index) { |
| 260 | if (!try self.eqlInner(child_a, child_b)) { | 224 | // result-id for both... |
| | 225 | continue; |
| | 226 | } else if (a_is_id) { |
| | 227 | // Both are IDs, so recurse. |
| | 228 | if (!try self.eqlInner(@enumFromInt(operand_a), @enumFromInt(operand_b))) { |
| | 229 | return false; |
| | 230 | } |
| | 231 | } else if (operand_a != operand_b) { |
| 261 | return false; | 232 | return false; |
| 262 | } | 233 | } |
| 263 | } | 234 | } |
| ... | @@ -290,11 +261,13 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule) !void { | ... | @@ -290,11 +261,13 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule) !void { |
| 290 | | 261 | |
| 291 | const info = try ModuleInfo.parse(a, parser, binary.*); | 262 | const info = try ModuleInfo.parse(a, parser, binary.*); |
| 292 | log.info("added {} entities", .{info.entities.count()}); | 263 | log.info("added {} entities", .{info.entities.count()}); |
| 293 | log.info("children size: {}", .{info.entity_children.len}); | | |
| 294 | log.info("extra data size: {}", .{info.extra_data.len}); | | |
| 295 | | 264 | |
| 296 | // Hash all keys once so that the maps can be allocated the right size. | 265 | // Hash all keys once so that the maps can be allocated the right size. |
| 297 | var ctx = EntityContext.init(a, &info); | 266 | var ctx = EntityContext{ |
| | 267 | .a = a, |
| | 268 | .info = &info, |
| | 269 | .binary = binary, |
| | 270 | }; |
| 298 | for (info.entities.keys()) |id| { | 271 | for (info.entities.keys()) |id| { |
| 299 | _ = try ctx.hash(id); | 272 | _ = try ctx.hash(id); |
| 300 | } | 273 | } |
| ... | @@ -318,7 +291,6 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule) !void { | ... | @@ -318,7 +291,6 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule) !void { |
| 318 | // Now process the module, and replace instructions where needed. | 291 | // Now process the module, and replace instructions where needed. |
| 319 | var section = Section{}; | 292 | var section = Section{}; |
| 320 | var it = binary.iterateInstructions(); | 293 | var it = binary.iterateInstructions(); |
| 321 | var id_offsets = std.ArrayList(u16).init(a); | | |
| 322 | var new_functions_section: ?usize = null; | 294 | var new_functions_section: ?usize = null; |
| 323 | var new_operands = std.ArrayList(u32).init(a); | 295 | var new_operands = std.ArrayList(u32).init(a); |
| 324 | var emitted_ptrs = std.AutoHashMap(ResultId, void).init(a); | 296 | var emitted_ptrs = std.AutoHashMap(ResultId, void).init(a); |
| ... | @@ -347,38 +319,31 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule) !void { | ... | @@ -347,38 +319,31 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule) !void { |
| 347 | | 319 | |
| 348 | // Re-emit the instruction, but replace all the IDs. | 320 | // Re-emit the instruction, but replace all the IDs. |
| 349 | | 321 | |
| 350 | id_offsets.items.len = 0; | | |
| 351 | try parser.parseInstructionResultIds(binary.*, inst, &id_offsets); | | |
| 352 | | | |
| 353 | new_operands.items.len = 0; | 322 | new_operands.items.len = 0; |
| 354 | try new_operands.appendSlice(inst.operands); | 323 | try new_operands.appendSlice(inst.operands); |
| 355 | for (id_offsets.items) |offset| { | 324 | |
| 356 | { | 325 | for (new_operands.items, 0..) |*operand, i| { |
| 357 | const id: ResultId = @enumFromInt(inst.operands[offset]); | 326 | const is_id = info.operand_is_id.isSet(inst.offset + 1 + i); |
| 358 | if (replace.get(id)) |new_id| { | 327 | if (!is_id) continue; |
| 359 | new_operands.items[offset] = @intFromEnum(new_id); | 328 | |
| 360 | } | 329 | if (replace.get(@enumFromInt(operand.*))) |new_id| { |
| | 330 | operand.* = @intFromEnum(new_id); |
| 361 | } | 331 | } |
| 362 | | 332 | |
| 363 | // TODO: Does this logic work? Maybe it will emit an OpTypeForwardPointer to | 333 | const id: ResultId = @enumFromInt(operand.*); |
| 364 | // something thats not a struct... | 334 | // TODO: This test is a little janky. Check the offset instead? |
| 365 | // It seems to work correctly on behavior.zig at least | | |
| 366 | const id: ResultId = @enumFromInt(new_operands.items[offset]); | | |
| 367 | if (maybe_result_id == null or maybe_result_id.? != id) { | 335 | if (maybe_result_id == null or maybe_result_id.? != id) { |
| 368 | const index = info.entities.getIndex(id) orelse continue; | 336 | const index = info.entities.getIndex(id) orelse continue; |
| 369 | const entity = info.entities.values()[index]; | 337 | const entity = info.entities.values()[index]; |
| 370 | if (entity.kind == .OpTypePointer) { | 338 | if (entity.kind == .OpTypePointer and !emitted_ptrs.contains(id)) { |
| 371 | if (!emitted_ptrs.contains(id)) { | 339 | // Grab the pointer's storage class from its operands in the original |
| 372 | // The storage class is in the extra data | 340 | // module. |
| 373 | // TODO: This is kind of hacky... | 341 | const storage_class: spec.StorageClass = @enumFromInt(binary.instructions[entity.first_operand + 1]); |
| 374 | const extra_data = info.extraDataByIndex(index); | 342 | try section.emit(a, .OpTypeForwardPointer, .{ |
| 375 | const storage_class: spec.StorageClass = @enumFromInt(extra_data[0]); | 343 | .pointer_type = id, |
| 376 | try section.emit(a, .OpTypeForwardPointer, .{ | 344 | .storage_class = storage_class, |
| 377 | .pointer_type = id, | 345 | }); |
| 378 | .storage_class = storage_class, | 346 | try emitted_ptrs.put(id, {}); |
| 379 | }); | | |
| 380 | try emitted_ptrs.put(id, {}); | | |
| 381 | } | | |
| 382 | } | 347 | } |
| 383 | } | 348 | } |
| 384 | } | 349 | } |