| ... | @@ -47,6 +47,10 @@ const ModuleInfo = struct { | ... | @@ -47,6 +47,10 @@ const ModuleInfo = struct { |
| 47 | result_id_index: u16, | 47 | result_id_index: u16, |
| 48 | /// The first decoration in `self.decorations`. | 48 | /// The first decoration in `self.decorations`. |
| 49 | first_decoration: u32, | 49 | first_decoration: u32, |
| | 50 | |
| | 51 | fn operands(self: Entity, binary: *const BinaryModule) []const Word { |
| | 52 | return binary.instructions[self.first_operand..][0..self.num_operands]; |
| | 53 | } |
| 50 | }; | 54 | }; |
| 51 | | 55 | |
| 52 | /// Maps result-id to Entity's | 56 | /// Maps result-id to Entity's |
| ... | @@ -210,10 +214,41 @@ const EntityContext = struct { | ... | @@ -210,10 +214,41 @@ const EntityContext = struct { |
| 210 | | 214 | |
| 211 | const entity = self.info.entities.values()[index]; | 215 | const entity = self.info.entities.values()[index]; |
| 212 | | 216 | |
| | 217 | // If the current pointer is recursive, don't immediately add it to the map. This is to ensure that |
| | 218 | // if the current pointer is already recursive, it gets the same hash a pointer that points to the |
| | 219 | // same child but has a different result-id. |
| 213 | if (entity.kind == .OpTypePointer) { | 220 | if (entity.kind == .OpTypePointer) { |
| 214 | // This may be either a pointer that is forward-referenced in the future, | 221 | // This may be either a pointer that is forward-referenced in the future, |
| 215 | // or a forward reference to a pointer. | 222 | // or a forward reference to a pointer. |
| 216 | const entry = try self.ptr_map_a.getOrPut(self.a, id); | 223 | // Note: We use the **struct** here instead of the pointer itself, to avoid an edge case like this: |
| | 224 | // |
| | 225 | // A - C*' |
| | 226 | // \ |
| | 227 | // C - C*' |
| | 228 | // / |
| | 229 | // B - C*" |
| | 230 | // |
| | 231 | // In this case, hashing A goes like |
| | 232 | // A -> C*' -> C -> C*' recursion |
| | 233 | // And hashing B goes like |
| | 234 | // B -> C*" -> C -> C*' -> C -> C*' recursion |
| | 235 | // The are several calls to ptrType in codegen that may C*' and C*" to be generated as separate |
| | 236 | // types. This is not a problem for C itself though - this can only be generated through resolveType() |
| | 237 | // and so ensures equality by Zig's type system. Technically the above problem is still present, but it |
| | 238 | // would only be present in a structure such as |
| | 239 | // |
| | 240 | // A - C*' - C' |
| | 241 | // \ |
| | 242 | // C*" - C - C* |
| | 243 | // / |
| | 244 | // B |
| | 245 | // |
| | 246 | // where there is a duplicate definition of struct C. Resolving this requires a much more time consuming |
| | 247 | // algorithm though, and because we don't expect any correctness issues with it, we leave that for now. |
| | 248 | |
| | 249 | // TODO: Do we need to mind the storage class here? Its going to be recursive regardless, right? |
| | 250 | const struct_id: ResultId = @enumFromInt(entity.operands(self.binary)[2]); |
| | 251 | const entry = try self.ptr_map_a.getOrPut(self.a, struct_id); |
| 217 | if (entry.found_existing) { | 252 | if (entry.found_existing) { |
| 218 | // Pointer already seen. Hash the index instead of recursing into its children. | 253 | // Pointer already seen. Hash the index instead of recursing into its children. |
| 219 | std.hash.autoHash(hasher, entry.index); | 254 | std.hash.autoHash(hasher, entry.index); |
| ... | @@ -228,12 +263,17 @@ const EntityContext = struct { | ... | @@ -228,12 +263,17 @@ const EntityContext = struct { |
| 228 | for (decorations) |decoration| { | 263 | for (decorations) |decoration| { |
| 229 | try self.hashEntity(hasher, decoration); | 264 | try self.hashEntity(hasher, decoration); |
| 230 | } | 265 | } |
| | 266 | |
| | 267 | if (entity.kind == .OpTypePointer) { |
| | 268 | const struct_id: ResultId = @enumFromInt(entity.operands(self.binary)[2]); |
| | 269 | assert(self.ptr_map_a.swapRemove(struct_id)); |
| | 270 | } |
| 231 | } | 271 | } |
| 232 | | 272 | |
| 233 | fn hashEntity(self: *EntityContext, hasher: *std.hash.Wyhash, entity: ModuleInfo.Entity) !void { | 273 | fn hashEntity(self: *EntityContext, hasher: *std.hash.Wyhash, entity: ModuleInfo.Entity) !void { |
| 234 | std.hash.autoHash(hasher, entity.kind); | 274 | std.hash.autoHash(hasher, entity.kind); |
| 235 | // Process operands | 275 | // Process operands |
| 236 | const operands = self.binary.instructions[entity.first_operand..][0..entity.num_operands]; | 276 | const operands = entity.operands(self.binary); |
| 237 | for (operands, 0..) |operand, i| { | 277 | for (operands, 0..) |operand, i| { |
| 238 | if (i == entity.result_id_index) { | 278 | if (i == entity.result_id_index) { |
| 239 | // Not relevant, skip... | 279 | // Not relevant, skip... |
| ... | @@ -273,12 +313,19 @@ const EntityContext = struct { | ... | @@ -273,12 +313,19 @@ const EntityContext = struct { |
| 273 | const entity_a = self.info.entities.values()[index_a]; | 313 | const entity_a = self.info.entities.values()[index_a]; |
| 274 | const entity_b = self.info.entities.values()[index_b]; | 314 | const entity_b = self.info.entities.values()[index_b]; |
| 275 | | 315 | |
| | 316 | if (entity_a.kind != entity_b.kind) { |
| | 317 | return false; |
| | 318 | } |
| | 319 | |
| 276 | if (entity_a.kind == .OpTypePointer) { | 320 | if (entity_a.kind == .OpTypePointer) { |
| 277 | // May be a forward reference, or should be saved as a potential | 321 | // May be a forward reference, or should be saved as a potential |
| 278 | // forward reference in the future. Whatever the case, it should | 322 | // forward reference in the future. Whatever the case, it should |
| 279 | // be the same for both a and b. | 323 | // be the same for both a and b. |
| 280 | const entry_a = try self.ptr_map_a.getOrPut(self.a, id_a); | 324 | const struct_id_a: ResultId = @enumFromInt(entity_a.operands(self.binary)[2]); |
| 281 | const entry_b = try self.ptr_map_b.getOrPut(self.a, id_b); | 325 | const struct_id_b: ResultId = @enumFromInt(entity_b.operands(self.binary)[2]); |
| | 326 | |
| | 327 | const entry_a = try self.ptr_map_a.getOrPut(self.a, struct_id_a); |
| | 328 | const entry_b = try self.ptr_map_b.getOrPut(self.a, struct_id_b); |
| 282 | | 329 | |
| 283 | if (entry_a.found_existing != entry_b.found_existing) return false; | 330 | if (entry_a.found_existing != entry_b.found_existing) return false; |
| 284 | if (entry_a.index != entry_b.index) return false; | 331 | if (entry_a.index != entry_b.index) return false; |
| ... | @@ -306,6 +353,14 @@ const EntityContext = struct { | ... | @@ -306,6 +353,14 @@ const EntityContext = struct { |
| 306 | } | 353 | } |
| 307 | } | 354 | } |
| 308 | | 355 | |
| | 356 | if (entity_a.kind == .OpTypePointer) { |
| | 357 | const struct_id_a: ResultId = @enumFromInt(entity_a.operands(self.binary)[2]); |
| | 358 | const struct_id_b: ResultId = @enumFromInt(entity_b.operands(self.binary)[2]); |
| | 359 | |
| | 360 | assert(self.ptr_map_a.swapRemove(struct_id_a)); |
| | 361 | assert(self.ptr_map_b.swapRemove(struct_id_b)); |
| | 362 | } |
| | 363 | |
| 309 | return true; | 364 | return true; |
| 310 | } | 365 | } |
| 311 | | 366 | |
| ... | @@ -316,8 +371,8 @@ const EntityContext = struct { | ... | @@ -316,8 +371,8 @@ const EntityContext = struct { |
| 316 | return false; | 371 | return false; |
| 317 | } | 372 | } |
| 318 | | 373 | |
| 319 | const operands_a = self.binary.instructions[entity_a.first_operand..][0..entity_a.num_operands]; | 374 | const operands_a = entity_a.operands(self.binary); |
| 320 | const operands_b = self.binary.instructions[entity_b.first_operand..][0..entity_b.num_operands]; | 375 | const operands_b = entity_b.operands(self.binary); |
| 321 | | 376 | |
| 322 | // Note: returns false for operands that have explicit defaults in optional operands... oh well | 377 | // Note: returns false for operands that have explicit defaults in optional operands... oh well |
| 323 | if (operands_a.len != operands_b.len) { | 378 | if (operands_a.len != operands_b.len) { |
| ... | @@ -463,7 +518,7 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: *std.P | ... | @@ -463,7 +518,7 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: *std.P |
| 463 | if (entity.kind == .OpTypePointer and !emitted_ptrs.contains(id)) { | 518 | if (entity.kind == .OpTypePointer and !emitted_ptrs.contains(id)) { |
| 464 | // Grab the pointer's storage class from its operands in the original | 519 | // Grab the pointer's storage class from its operands in the original |
| 465 | // module. | 520 | // module. |
| 466 | const storage_class: spec.StorageClass = @enumFromInt(binary.instructions[entity.first_operand + 1]); | 521 | const storage_class: spec.StorageClass = @enumFromInt(entity.operands(binary)[1]); |
| 467 | try section.emit(a, .OpTypeForwardPointer, .{ | 522 | try section.emit(a, .OpTypeForwardPointer, .{ |
| 468 | .pointer_type = id, | 523 | .pointer_type = id, |
| 469 | .storage_class = storage_class, | 524 | .storage_class = storage_class, |