| ... | ... | @@ -47,6 +47,10 @@ const ModuleInfo = struct { |
| 47 | 47 | result_id_index: u16, |
| 48 | 48 | /// The first decoration in `self.decorations`. |
| 49 | 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 | 56 | /// Maps result-id to Entity's |
| ... | ... | @@ -210,10 +214,41 @@ const EntityContext = struct { |
| 210 | 214 | |
| 211 | 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 | 220 | if (entity.kind == .OpTypePointer) { |
| 214 | 221 | // This may be either a pointer that is forward-referenced in the future, |
| 215 | 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 | 252 | if (entry.found_existing) { |
| 218 | 253 | // Pointer already seen. Hash the index instead of recursing into its children. |
| 219 | 254 | std.hash.autoHash(hasher, entry.index); |
| ... | ... | @@ -228,12 +263,17 @@ const EntityContext = struct { |
| 228 | 263 | for (decorations) |decoration| { |
| 229 | 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 | 273 | fn hashEntity(self: *EntityContext, hasher: *std.hash.Wyhash, entity: ModuleInfo.Entity) !void { |
| 234 | 274 | std.hash.autoHash(hasher, entity.kind); |
| 235 | 275 | // Process operands |
| 236 | | const operands = self.binary.instructions[entity.first_operand..][0..entity.num_operands]; |
| 276 | const operands = entity.operands(self.binary); |
| 237 | 277 | for (operands, 0..) |operand, i| { |
| 238 | 278 | if (i == entity.result_id_index) { |
| 239 | 279 | // Not relevant, skip... |
| ... | ... | @@ -273,12 +313,19 @@ const EntityContext = struct { |
| 273 | 313 | const entity_a = self.info.entities.values()[index_a]; |
| 274 | 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 | 320 | if (entity_a.kind == .OpTypePointer) { |
| 277 | 321 | // May be a forward reference, or should be saved as a potential |
| 278 | 322 | // forward reference in the future. Whatever the case, it should |
| 279 | 323 | // be the same for both a and b. |
| 280 | | const entry_a = try self.ptr_map_a.getOrPut(self.a, id_a); |
| 281 | | const entry_b = try self.ptr_map_b.getOrPut(self.a, id_b); |
| 324 | const struct_id_a: ResultId = @enumFromInt(entity_a.operands(self.binary)[2]); |
| 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 | 330 | if (entry_a.found_existing != entry_b.found_existing) return false; |
| 284 | 331 | if (entry_a.index != entry_b.index) return false; |
| ... | ... | @@ -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 | 364 | return true; |
| 310 | 365 | } |
| 311 | 366 | |
| ... | ... | @@ -316,8 +371,8 @@ const EntityContext = struct { |
| 316 | 371 | return false; |
| 317 | 372 | } |
| 318 | 373 | |
| 319 | | const operands_a = self.binary.instructions[entity_a.first_operand..][0..entity_a.num_operands]; |
| 320 | | const operands_b = self.binary.instructions[entity_b.first_operand..][0..entity_b.num_operands]; |
| 374 | const operands_a = entity_a.operands(self.binary); |
| 375 | const operands_b = entity_b.operands(self.binary); |
| 321 | 376 | |
| 322 | 377 | // Note: returns false for operands that have explicit defaults in optional operands... oh well |
| 323 | 378 | if (operands_a.len != operands_b.len) { |
| ... | ... | @@ -463,7 +518,7 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: *std.P |
| 463 | 518 | if (entity.kind == .OpTypePointer and !emitted_ptrs.contains(id)) { |
| 464 | 519 | // Grab the pointer's storage class from its operands in the original |
| 465 | 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 | 522 | try section.emit(a, .OpTypeForwardPointer, .{ |
| 468 | 523 | .pointer_type = id, |
| 469 | 524 | .storage_class = storage_class, |