| ... | ... | @@ -136,13 +136,10 @@ air: Air, |
| 136 | 136 | liveness: Air.Liveness, |
| 137 | 137 | gpa: mem.Allocator, |
| 138 | 138 | func_index: InternPool.Index, |
| 139 | | /// Contains a list of current branches. |
| 140 | | /// When we return from a branch, the branch will be popped from this list, |
| 141 | | /// which means branches can only contain references from within its own branch, |
| 142 | | /// or a branch higher (lower index) in the tree. |
| 143 | | branches: std.ArrayList(Branch) = .empty, |
| 139 | /// Contains a list of current branches. Stores LocalPool before entering branch. |
| 140 | branches: std.ArrayList(LocalPool) = .empty, |
| 144 | 141 | /// Table to save `WValue`'s generated by an `Air.Inst` |
| 145 | | // values: ValueTable, |
| 142 | values: ValueTable = .empty, |
| 146 | 143 | /// Mapping from Air.Inst.Index to block ids |
| 147 | 144 | blocks: std.array_hash_map.Auto(Air.Inst.Index, struct { |
| 148 | 145 | label: u32, |
| ... | ... | @@ -150,10 +147,6 @@ blocks: std.array_hash_map.Auto(Air.Inst.Index, struct { |
| 150 | 147 | }) = .{}, |
| 151 | 148 | /// Maps `loop` instructions to their label. `br` to here repeats the loop. |
| 152 | 149 | loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty, |
| 153 | | /// The index the next local generated will have |
| 154 | | /// NOTE: arguments share the index with locals therefore the first variable |
| 155 | | /// will have the index that comes after the last argument's index |
| 156 | | local_index: u32, |
| 157 | 150 | /// The index of the current argument. |
| 158 | 151 | /// Used to track which argument is being referenced in `airArg`. |
| 159 | 152 | arg_index: u32 = 0, |
| ... | ... | @@ -207,23 +200,8 @@ stack_size: u32 = 0, |
| 207 | 200 | /// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default. |
| 208 | 201 | stack_alignment: Alignment = .@"16", |
| 209 | 202 | |
| 210 | | // For each individual Wasm valtype we store a seperate free list which |
| 211 | | // allows us to re-use locals that are no longer used. e.g. a temporary local. |
| 212 | | /// A list of indexes which represents a local of valtype `i32`. |
| 213 | | /// It is illegal to store a non-i32 valtype in this list. |
| 214 | | free_locals_i32: std.ArrayList(u32) = .empty, |
| 215 | | /// A list of indexes which represents a local of valtype `i64`. |
| 216 | | /// It is illegal to store a non-i64 valtype in this list. |
| 217 | | free_locals_i64: std.ArrayList(u32) = .empty, |
| 218 | | /// A list of indexes which represents a local of valtype `f32`. |
| 219 | | /// It is illegal to store a non-f32 valtype in this list. |
| 220 | | free_locals_f32: std.ArrayList(u32) = .empty, |
| 221 | | /// A list of indexes which represents a local of valtype `f64`. |
| 222 | | /// It is illegal to store a non-f64 valtype in this list. |
| 223 | | free_locals_f64: std.ArrayList(u32) = .empty, |
| 224 | | /// A list of indexes which represents a local of valtype `v127`. |
| 225 | | /// It is illegal to store a non-v128 valtype in this list. |
| 226 | | free_locals_v128: std.ArrayList(u32) = .empty, |
| 203 | local_pool: LocalPool = .{}, |
| 204 | wasm_param_count: u32 = undefined, |
| 227 | 205 | |
| 228 | 206 | /// When in debug mode, this tracks if no `finishAir` was missed. |
| 229 | 207 | /// Forgetting to call `finishAir` will cause the result to not be |
| ... | ... | @@ -239,13 +217,7 @@ const WValue = union(enum) { |
| 239 | 217 | none: void, |
| 240 | 218 | /// The value lives on top of the stack |
| 241 | 219 | stack: void, |
| 242 | | /// Index of the local |
| 243 | | local: struct { |
| 244 | | /// Contains the index to the local |
| 245 | | value: u32, |
| 246 | | /// The amount of instructions referencing this `WValue` |
| 247 | | references: u32, |
| 248 | | }, |
| 220 | local: u32, // index |
| 249 | 221 | /// An immediate 32bit value |
| 250 | 222 | imm32: u32, |
| 251 | 223 | /// An immediate 64bit value |
| ... | ... | @@ -290,7 +262,7 @@ const WValue = union(enum) { |
| 290 | 262 | switch (value) { |
| 291 | 263 | .stack => { |
| 292 | 264 | const new_local = try gen.allocLocal(ty); |
| 293 | | try gen.addLocal(.local_set, new_local.local.value); |
| 265 | try gen.addLocal(.local_set, new_local.local); |
| 294 | 266 | return new_local; |
| 295 | 267 | }, |
| 296 | 268 | else => return value, |
| ... | ... | @@ -299,23 +271,9 @@ const WValue = union(enum) { |
| 299 | 271 | |
| 300 | 272 | /// Marks a local as no longer being referenced and essentially allows |
| 301 | 273 | /// us to re-use it somewhere else within the function. |
| 302 | | /// The valtype of the local is deducted by using the index of the given `WValue`. |
| 303 | | fn free(value: *WValue, gen: *CodeGen) void { |
| 274 | fn free(value: *WValue, cg: *CodeGen) void { |
| 304 | 275 | if (value.* != .local) return; |
| 305 | | const local_value = value.local.value; |
| 306 | | const reserved = gen.args.len + @intFromBool(gen.return_value != .none) + @intFromBool(gen.varargs != .none); |
| 307 | | if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals. |
| 308 | | |
| 309 | | const index = local_value - reserved; |
| 310 | | const valtype = gen.mir_locals.items[index]; |
| 311 | | switch (valtype) { |
| 312 | | .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead |
| 313 | | .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, |
| 314 | | .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return, |
| 315 | | .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return, |
| 316 | | .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return, |
| 317 | | } |
| 318 | | log.debug("freed local ({d}) of type {}", .{ local_value, valtype }); |
| 276 | cg.releaseLocal(value.local); |
| 319 | 277 | value.* = .dead; |
| 320 | 278 | } |
| 321 | 279 | }; |
| ... | ... | @@ -323,6 +281,77 @@ const WValue = union(enum) { |
| 323 | 281 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` |
| 324 | 282 | const ValueTable = std.array_hash_map.Auto(Air.Inst.Ref, WValue); |
| 325 | 283 | |
| 284 | const LocalPool = struct { |
| 285 | const OptionalIndex = enum(u32) { |
| 286 | none = math.maxInt(u32), |
| 287 | _, |
| 288 | |
| 289 | fn init(index: u32) OptionalIndex { |
| 290 | const result: OptionalIndex = @fromBackingInt(index); |
| 291 | assert(result != .none); |
| 292 | return result; |
| 293 | } |
| 294 | |
| 295 | fn unwrap(index: OptionalIndex) ?u32 { |
| 296 | return if (index == .none) null else @backingInt(index); |
| 297 | } |
| 298 | }; |
| 299 | |
| 300 | const Local = union { |
| 301 | ref_count: u32, |
| 302 | next_free: OptionalIndex, |
| 303 | }; |
| 304 | |
| 305 | locals: std.ArrayList(Local) = .empty, |
| 306 | free_i32: OptionalIndex = .none, |
| 307 | free_i64: OptionalIndex = .none, |
| 308 | free_f32: OptionalIndex = .none, |
| 309 | free_f64: OptionalIndex = .none, |
| 310 | free_v128: OptionalIndex = .none, |
| 311 | |
| 312 | fn deinit(pool: *LocalPool, gpa: Allocator) void { |
| 313 | pool.locals.deinit(gpa); |
| 314 | pool.* = undefined; |
| 315 | } |
| 316 | |
| 317 | fn clone(pool: *LocalPool, gpa: Allocator) Allocator.Error!LocalPool { |
| 318 | return .{ |
| 319 | .locals = try pool.locals.clone(gpa), |
| 320 | .free_i32 = pool.free_i32, |
| 321 | .free_i64 = pool.free_i64, |
| 322 | .free_f32 = pool.free_f32, |
| 323 | .free_f64 = pool.free_f64, |
| 324 | .free_v128 = pool.free_v128, |
| 325 | }; |
| 326 | } |
| 327 | |
| 328 | fn freeList(pool: *LocalPool, valtype: std.wasm.Valtype) *OptionalIndex { |
| 329 | return switch (valtype) { |
| 330 | .i32 => &pool.free_i32, |
| 331 | .i64 => &pool.free_i64, |
| 332 | .f32 => &pool.free_f32, |
| 333 | .f64 => &pool.free_f64, |
| 334 | .v128 => &pool.free_v128, |
| 335 | }; |
| 336 | } |
| 337 | |
| 338 | fn restore(snapshot: *LocalPool, cg: *CodeGen) void { |
| 339 | const local_pool = &cg.local_pool; |
| 340 | const old_local_count = snapshot.locals.items.len; |
| 341 | assert(local_pool.locals.items.len >= old_local_count); |
| 342 | @memcpy(local_pool.locals.items[0..old_local_count], snapshot.locals.items); |
| 343 | local_pool.free_i32 = snapshot.free_i32; |
| 344 | local_pool.free_i64 = snapshot.free_i64; |
| 345 | local_pool.free_f32 = snapshot.free_f32; |
| 346 | local_pool.free_f64 = snapshot.free_f64; |
| 347 | local_pool.free_v128 = snapshot.free_v128; |
| 348 | |
| 349 | for (old_local_count..local_pool.locals.items.len) |index| { |
| 350 | cg.appendFreeLocal(cg.wasm_param_count + @as(u32, @intCast(index))); |
| 351 | } |
| 352 | } |
| 353 | }; |
| 354 | |
| 326 | 355 | const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 327 | 356 | |
| 328 | 357 | const InnerError = Error || error{ |
| ... | ... | @@ -336,14 +365,11 @@ pub fn deinit(cg: *CodeGen) void { |
| 336 | 365 | const gpa = cg.gpa; |
| 337 | 366 | for (cg.branches.items) |*branch| branch.deinit(gpa); |
| 338 | 367 | cg.branches.deinit(gpa); |
| 368 | cg.values.deinit(gpa); |
| 339 | 369 | cg.blocks.deinit(gpa); |
| 340 | 370 | cg.loops.deinit(gpa); |
| 341 | 371 | cg.simd_immediates.deinit(gpa); |
| 342 | | cg.free_locals_i32.deinit(gpa); |
| 343 | | cg.free_locals_i64.deinit(gpa); |
| 344 | | cg.free_locals_f32.deinit(gpa); |
| 345 | | cg.free_locals_f64.deinit(gpa); |
| 346 | | cg.free_locals_v128.deinit(gpa); |
| 372 | cg.local_pool.deinit(gpa); |
| 347 | 373 | cg.mir_instructions.deinit(gpa); |
| 348 | 374 | cg.mir_extra.deinit(gpa); |
| 349 | 375 | cg.mir_locals.deinit(gpa); |
| ... | ... | @@ -362,19 +388,11 @@ pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) Error { |
| 362 | 388 | /// Resolves the `WValue` for the given instruction `inst` |
| 363 | 389 | /// When the given instruction has a `Value`, it returns a constant instead |
| 364 | 390 | fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { |
| 365 | | var branch_index = cg.branches.items.len; |
| 366 | | while (branch_index > 0) : (branch_index -= 1) { |
| 367 | | const branch = cg.branches.items[branch_index - 1]; |
| 368 | | if (branch.values.get(ref)) |value| { |
| 369 | | return value; |
| 370 | | } |
| 371 | | } |
| 391 | if (cg.values.get(ref)) |value| return value; |
| 372 | 392 | |
| 373 | 393 | // when we did not find an existing instruction, it |
| 374 | 394 | // means we must generate it from a constant. |
| 375 | | // We always store constants in the most outer branch as they must never |
| 376 | | // be removed. The most outer branch is always at index 0. |
| 377 | | const gop = try cg.branches.items[0].values.getOrPut(cg.gpa, ref); |
| 395 | const gop = try cg.values.getOrPut(cg.gpa, ref); |
| 378 | 396 | assert(!gop.found_existing); |
| 379 | 397 | |
| 380 | 398 | const pt = cg.pt; |
| ... | ... | @@ -413,20 +431,33 @@ fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue { |
| 413 | 431 | |
| 414 | 432 | /// NOTE: if result == .stack, it will be stored in .local |
| 415 | 433 | fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void { |
| 434 | return cg.finishAirWithBranchDeaths(inst, result, operands, &.{}); |
| 435 | } |
| 436 | |
| 437 | /// Takes `branch_deaths` from continuing branch and calls `processDeath` after result retaining logic. |
| 438 | fn finishAirWithBranchDeaths( |
| 439 | cg: *CodeGen, |
| 440 | inst: Air.Inst.Index, |
| 441 | result: WValue, |
| 442 | operands: []const Air.Inst.Ref, |
| 443 | branch_deaths: []const Air.Inst.Index, |
| 444 | ) InnerError!void { |
| 416 | 445 | assert(operands.len <= Air.Liveness.bpi - 1); |
| 417 | 446 | |
| 418 | | var final_result = result; |
| 419 | | const aliased_operand: ?*WValue = if (result == .local) aliased: { |
| 447 | if (result == .local) aliased: { |
| 420 | 448 | for (operands) |operand| { |
| 421 | 449 | if (operand.toIndex() == null) continue; |
| 422 | | const resolved_operand = cg.getResolvedInst(operand); |
| 423 | | if (resolved_operand.* != .local or resolved_operand.local.value != result.local.value) continue; |
| 450 | const resolved_operand = cg.values.getPtr(operand) orelse unreachable; |
| 451 | if (resolved_operand.* != .local or resolved_operand.local != result.local) continue; |
| 424 | 452 | |
| 425 | | resolved_operand.local.references += 1; |
| 426 | | break :aliased resolved_operand; |
| 453 | if (result.local >= cg.wasm_param_count) { |
| 454 | const local = &cg.local_pool.locals.items[result.local - cg.wasm_param_count]; |
| 455 | assert(local.ref_count > 0); |
| 456 | local.ref_count += 1; |
| 457 | } |
| 458 | break :aliased; |
| 427 | 459 | } |
| 428 | | break :aliased null; |
| 429 | | } else null; |
| 460 | } |
| 430 | 461 | |
| 431 | 462 | var tomb_bits = cg.liveness.getTombBits(inst); |
| 432 | 463 | for (operands) |operand| { |
| ... | ... | @@ -435,8 +466,8 @@ fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []con |
| 435 | 466 | if (!dies) continue; |
| 436 | 467 | processDeath(cg, operand); |
| 437 | 468 | } |
| 438 | | if (aliased_operand) |operand| final_result.local.references = operand.local.references; |
| 439 | | try cg.finishAirResult(inst, final_result); |
| 469 | for (branch_deaths) |death| cg.processDeath(death.toRef()); |
| 470 | try cg.finishAirResult(inst, result); |
| 440 | 471 | } |
| 441 | 472 | |
| 442 | 473 | fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerError!void { |
| ... | ... | @@ -446,8 +477,7 @@ fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerErro |
| 446 | 477 | result |
| 447 | 478 | else |
| 448 | 479 | try result.toLocal(cg, cg.typeOfIndex(inst)); |
| 449 | | const branch = cg.currentBranch(); |
| 450 | | branch.values.putAssumeCapacityNoClobber(inst.toRef(), trackable_result); |
| 480 | try cg.values.putNoClobber(cg.gpa, inst.toRef(), trackable_result); |
| 451 | 481 | } |
| 452 | 482 | |
| 453 | 483 | if (std.debug.runtime_safety) { |
| ... | ... | @@ -455,17 +485,16 @@ fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerErro |
| 455 | 485 | } |
| 456 | 486 | } |
| 457 | 487 | |
| 458 | | const Branch = struct { |
| 459 | | values: ValueTable = .{}, |
| 460 | | |
| 461 | | fn deinit(branch: *Branch, gpa: Allocator) void { |
| 462 | | branch.values.deinit(gpa); |
| 463 | | branch.* = undefined; |
| 464 | | } |
| 465 | | }; |
| 488 | fn pushBranch(cg: *CodeGen) InnerError!void { |
| 489 | var snapshot = try cg.local_pool.clone(cg.gpa); |
| 490 | errdefer snapshot.deinit(cg.gpa); |
| 491 | try cg.branches.append(cg.gpa, snapshot); |
| 492 | } |
| 466 | 493 | |
| 467 | | inline fn currentBranch(cg: *CodeGen) *Branch { |
| 468 | | return &cg.branches.items[cg.branches.items.len - 1]; |
| 494 | fn popBranch(cg: *CodeGen) void { |
| 495 | var snapshot = cg.branches.pop().?; |
| 496 | defer snapshot.deinit(cg.gpa); |
| 497 | snapshot.restore(cg); |
| 469 | 498 | } |
| 470 | 499 | |
| 471 | 500 | fn feed(cg: *CodeGen, bt: *Air.Liveness.BigTomb, operand: Air.Inst.Ref) void { |
| ... | ... | @@ -476,20 +505,9 @@ fn feed(cg: *CodeGen, bt: *Air.Liveness.BigTomb, operand: Air.Inst.Ref) void { |
| 476 | 505 | |
| 477 | 506 | fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void { |
| 478 | 507 | if (ref.toIndex() == null) return; |
| 479 | | // Branches are currently only allowed to free locals allocated |
| 480 | | // within their own branch. |
| 481 | | // TODO: Upon branch consolidation free any locals if needed. |
| 482 | | const value = cg.currentBranch().values.getPtr(ref) orelse return; |
| 508 | const value = cg.values.getPtr(ref) orelse unreachable; |
| 483 | 509 | if (value.* != .local) return; |
| 484 | | const reserved_indexes = cg.args.len + @intFromBool(cg.return_value != .none); |
| 485 | | if (value.local.value < reserved_indexes) { |
| 486 | | return; // function arguments can never be re-used |
| 487 | | } |
| 488 | | log.debug("Decreasing reference for ref: %{d}, using local '{d}'", .{ @backingInt(ref.toIndex().?), value.local.value }); |
| 489 | | value.local.references -= 1; |
| 490 | | if (value.local.references == 0) { |
| 491 | | value.free(cg); |
| 492 | | } |
| 510 | cg.releaseLocal(value.local); |
| 493 | 511 | } |
| 494 | 512 | |
| 495 | 513 | pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { |
| ... | ... | @@ -708,7 +726,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { |
| 708 | 726 | switch (value) { |
| 709 | 727 | .dead => unreachable, // reference to free'd `WValue` |
| 710 | 728 | .none, .stack => {}, // no-op |
| 711 | | .local => |idx| try cg.addLocal(.local_get, idx.value), |
| 729 | .local => |idx| try cg.addLocal(.local_get, idx), |
| 712 | 730 | .imm32 => |val| try cg.addImm32(val), |
| 713 | 731 | .imm64 => |val| try cg.addImm64(val), |
| 714 | 732 | .imm128 => |val| try cg.addImm128(val), |
| ... | ... | @@ -780,22 +798,28 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { |
| 780 | 798 | /// Emits the base address for a memory instruction. |
| 781 | 799 | fn emitMemBase(cg: *CodeGen, ptr: WValue) InnerError!void { |
| 782 | 800 | switch (ptr) { |
| 783 | | .stack_offset => try cg.addLocal(.local_get, cg.bottom_stack_value.local.value), |
| 801 | .stack_offset => try cg.addLocal(.local_get, cg.bottom_stack_value.local), |
| 784 | 802 | else => try cg.emitWValue(ptr), |
| 785 | 803 | } |
| 786 | 804 | } |
| 787 | 805 | |
| 788 | | /// From a reference, returns its resolved `WValue`. |
| 789 | | /// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet. |
| 790 | | fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue { |
| 791 | | var index = cg.branches.items.len; |
| 792 | | while (index > 0) : (index -= 1) { |
| 793 | | const branch = cg.branches.items[index - 1]; |
| 794 | | if (branch.values.getPtr(ref)) |value| { |
| 795 | | return value; |
| 796 | | } |
| 797 | | } |
| 798 | | unreachable; // developer-error: This can only be called on resolved instructions. Use `resolveInst` instead. |
| 806 | fn appendFreeLocal(cg: *CodeGen, index: u32) void { |
| 807 | assert(index >= cg.wasm_param_count); |
| 808 | const mir_index = index - cg.wasm_param_count; |
| 809 | const valtype = cg.mir_locals.items[mir_index]; |
| 810 | const free_list = cg.local_pool.freeList(valtype); |
| 811 | cg.local_pool.locals.items[mir_index] = .{ .next_free = free_list.* }; |
| 812 | free_list.* = .init(index); |
| 813 | log.debug("freed local ({d}) of type {}", .{ index, valtype }); |
| 814 | } |
| 815 | |
| 816 | fn releaseLocal(cg: *CodeGen, index: u32) void { |
| 817 | if (index < cg.wasm_param_count) return; |
| 818 | const local = &cg.local_pool.locals.items[index - cg.wasm_param_count]; |
| 819 | assert(local.ref_count > 0); |
| 820 | local.ref_count -= 1; |
| 821 | if (local.ref_count > 0) return; |
| 822 | cg.appendFreeLocal(index); |
| 799 | 823 | } |
| 800 | 824 | |
| 801 | 825 | /// Creates one locals for a given `Type`. |
| ... | ... | @@ -803,16 +827,13 @@ fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue { |
| 803 | 827 | fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 804 | 828 | const zcu = cg.pt.zcu; |
| 805 | 829 | const valtype = typeToValtype(ty, zcu, cg.target); |
| 806 | | const index_or_null = switch (valtype) { |
| 807 | | .i32 => cg.free_locals_i32.pop(), |
| 808 | | .i64 => cg.free_locals_i64.pop(), |
| 809 | | .f32 => cg.free_locals_f32.pop(), |
| 810 | | .f64 => cg.free_locals_f64.pop(), |
| 811 | | .v128 => cg.free_locals_v128.pop(), |
| 812 | | }; |
| 813 | | if (index_or_null) |index| { |
| 830 | const free_list = cg.local_pool.freeList(valtype); |
| 831 | if (free_list.unwrap()) |index| { |
| 832 | const local = &cg.local_pool.locals.items[index - cg.wasm_param_count]; |
| 833 | free_list.* = local.next_free; |
| 834 | local.* = .{ .ref_count = 1 }; |
| 814 | 835 | log.debug("reusing local ({d}) of type {}", .{ index, valtype }); |
| 815 | | return .{ .local = .{ .value = index, .references = 1 } }; |
| 836 | return .{ .local = index }; |
| 816 | 837 | } |
| 817 | 838 | log.debug("new local of type {}", .{valtype}); |
| 818 | 839 | return cg.ensureAllocLocal(ty); |
| ... | ... | @@ -822,10 +843,13 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 822 | 843 | /// to use a zero-initialized local. |
| 823 | 844 | fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 824 | 845 | const zcu = cg.pt.zcu; |
| 825 | | try cg.mir_locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target)); |
| 826 | | const initial_index = cg.local_index; |
| 827 | | cg.local_index += 1; |
| 828 | | return .{ .local = .{ .value = initial_index, .references = 1 } }; |
| 846 | try cg.mir_locals.ensureUnusedCapacity(cg.gpa, 1); |
| 847 | try cg.local_pool.locals.ensureUnusedCapacity(cg.gpa, 1); |
| 848 | assert(cg.mir_locals.items.len == cg.local_pool.locals.items.len); |
| 849 | const initial_index = cg.wasm_param_count + @as(u32, @intCast(cg.local_pool.locals.items.len)); |
| 850 | cg.mir_locals.appendAssumeCapacity(typeToValtype(ty, zcu, cg.target)); |
| 851 | cg.local_pool.locals.appendAssumeCapacity(.{ .ref_count = 1 }); |
| 852 | return .{ .local = initial_index }; |
| 829 | 853 | } |
| 830 | 854 | |
| 831 | 855 | pub const Error = codegen.Error; |
| ... | ... | @@ -867,7 +891,6 @@ pub fn generate( |
| 867 | 891 | .args = cc_result.args, |
| 868 | 892 | .return_value = cc_result.return_value, |
| 869 | 893 | .varargs = cc_result.varargs, |
| 870 | | .local_index = cc_result.local_index, |
| 871 | 894 | .mir_instructions = .empty, |
| 872 | 895 | .mir_extra = .empty, |
| 873 | 896 | .mir_locals = .empty, |
| ... | ... | @@ -890,17 +913,10 @@ pub fn generate( |
| 890 | 913 | |
| 891 | 914 | fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { |
| 892 | 915 | const zcu = cg.pt.zcu; |
| 893 | | // branch used for const values |
| 894 | | try cg.branches.append(cg.gpa, .{}); |
| 895 | | // func scope branch |
| 896 | | try cg.branches.append(cg.gpa, .{}); |
| 897 | | defer { |
| 898 | | var func_branch = cg.branches.pop().?; |
| 899 | | func_branch.deinit(cg.gpa); |
| 900 | | var const_branch = cg.branches.pop().?; |
| 901 | | const_branch.deinit(cg.gpa); |
| 902 | | assert(cg.branches.items.len == 0); // missing branch merge |
| 903 | | } |
| 916 | cg.wasm_param_count = @intCast(cg.args.len + @intFromBool(cg.return_value != .none) + @intFromBool(cg.varargs != .none)); |
| 917 | cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize); |
| 918 | cg.bottom_stack_value = try cg.ensureAllocLocal(Type.usize); |
| 919 | defer assert(cg.branches.items.len == 0); |
| 904 | 920 | // Generate MIR for function body |
| 905 | 921 | try cg.genBody(cg.air.getMainBody()); |
| 906 | 922 | |
| ... | ... | @@ -925,11 +941,11 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { |
| 925 | 941 | .instructions = cg.mir_instructions.toOwnedSlice(), |
| 926 | 942 | .extra = cg.mir_extra.toOwnedSliceAssert(), |
| 927 | 943 | .locals = cg.mir_locals.toOwnedSliceAssert(), |
| 928 | | .prologue = if (cg.initial_stack_value == .none) .none else .{ |
| 929 | | .sp_local = cg.initial_stack_value.local.value, |
| 944 | .prologue = .{ |
| 945 | .sp_local = cg.initial_stack_value.local, |
| 930 | 946 | .flags = .{ .stack_alignment = cg.stack_alignment }, |
| 931 | 947 | .stack_size = cg.stack_size, |
| 932 | | .bottom_stack_local = cg.bottom_stack_value.local.value, |
| 948 | .bottom_stack_local = cg.bottom_stack_value.local, |
| 933 | 949 | }, |
| 934 | 950 | .uavs = cg.mir_uavs.move(), |
| 935 | 951 | .indirect_function_set = cg.mir_indirect_function_set.move(), |
| ... | ... | @@ -976,7 +992,7 @@ fn resolveCallingConventionValues( |
| 976 | 992 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, target)) { |
| 977 | 993 | // the sret arg will be passed as first argument, therefore we |
| 978 | 994 | // set the `return_value` before allocating locals for regular args. |
| 979 | | result.return_value = .{ .local = .{ .value = result.local_index, .references = 1 } }; |
| 995 | result.return_value = .{ .local = result.local_index }; |
| 980 | 996 | result.local_index += 1; |
| 981 | 997 | } |
| 982 | 998 | |
| ... | ... | @@ -987,7 +1003,7 @@ fn resolveCallingConventionValues( |
| 987 | 1003 | continue; |
| 988 | 1004 | } |
| 989 | 1005 | |
| 990 | | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); |
| 1006 | try args.append(.{ .local = result.local_index }); |
| 991 | 1007 | result.local_index += 1; |
| 992 | 1008 | } |
| 993 | 1009 | }, |
| ... | ... | @@ -1000,17 +1016,17 @@ fn resolveCallingConventionValues( |
| 1000 | 1016 | |
| 1001 | 1017 | switch (abi.classifyType(param_ty, zcu, target)) { |
| 1002 | 1018 | .direct, .indirect => { |
| 1003 | | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); |
| 1019 | try args.append(.{ .local = result.local_index }); |
| 1004 | 1020 | result.local_index += 1; |
| 1005 | 1021 | }, |
| 1006 | 1022 | .double_i64 => { |
| 1007 | | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); |
| 1008 | | try args.append(.{ .local = .{ .value = result.local_index + 1, .references = 1 } }); |
| 1023 | try args.append(.{ .local = result.local_index }); |
| 1024 | try args.append(.{ .local = result.local_index + 1 }); |
| 1009 | 1025 | result.local_index += 2; |
| 1010 | 1026 | }, |
| 1011 | 1027 | .unrolled => |vector| { |
| 1012 | 1028 | for (0..vector.len) |_| { |
| 1013 | | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); |
| 1029 | try args.append(.{ .local = result.local_index }); |
| 1014 | 1030 | result.local_index += 1; |
| 1015 | 1031 | } |
| 1016 | 1032 | }, |
| ... | ... | @@ -1021,7 +1037,7 @@ fn resolveCallingConventionValues( |
| 1021 | 1037 | } |
| 1022 | 1038 | |
| 1023 | 1039 | if (fn_info.is_var_args) { |
| 1024 | | result.varargs = .{ .local = .{ .value = result.local_index, .references = 1 } }; |
| 1040 | result.varargs = .{ .local = result.local_index }; |
| 1025 | 1041 | result.local_index += 1; |
| 1026 | 1042 | } |
| 1027 | 1043 | |
| ... | ... | @@ -1088,23 +1104,9 @@ fn lowerArg(cg: *CodeGen, cc: std.lang.CallingConvention, ty: Type, value: WValu |
| 1088 | 1104 | } |
| 1089 | 1105 | } |
| 1090 | 1106 | |
| 1091 | | /// Creates a local for the initial stack value |
| 1092 | | /// Asserts `initial_stack_value` is `.none` |
| 1093 | | fn initializeStack(cg: *CodeGen) !void { |
| 1094 | | assert(cg.initial_stack_value == .none); |
| 1095 | | // Reserve a local to store the current stack pointer |
| 1096 | | // We can later use this local to set the stack pointer back to the value |
| 1097 | | // we have stored here. |
| 1098 | | cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize); |
| 1099 | | // Also reserve a local to store the bottom stack value |
| 1100 | | cg.bottom_stack_value = try cg.ensureAllocLocal(Type.usize); |
| 1101 | | } |
| 1102 | | |
| 1103 | 1107 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it |
| 1104 | 1108 | /// to the global stack pointer variable |
| 1105 | 1109 | fn restoreStackPointer(cg: *CodeGen) !void { |
| 1106 | | // only restore the pointer if it was initialized |
| 1107 | | if (cg.initial_stack_value == .none) return; |
| 1108 | 1110 | // Get the original stack pointer's value |
| 1109 | 1111 | try cg.emitWValue(cg.initial_stack_value); |
| 1110 | 1112 | |
| ... | ... | @@ -1144,10 +1146,6 @@ fn allocInt(cg: *CodeGen, int_ty: IntType) !WValue { |
| 1144 | 1146 | fn allocStackBytes(cg: *CodeGen, size: u32, alignment: Alignment) !WValue { |
| 1145 | 1147 | assert(size > 0); |
| 1146 | 1148 | |
| 1147 | | if (cg.initial_stack_value == .none) { |
| 1148 | | try cg.initializeStack(); |
| 1149 | | } |
| 1150 | | |
| 1151 | 1149 | cg.stack_alignment = cg.stack_alignment.max(alignment); |
| 1152 | 1150 | |
| 1153 | 1151 | const offset: u32 = @intCast(alignment.forward(cg.stack_size)); |
| ... | ... | @@ -1166,10 +1164,6 @@ fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue { |
| 1166 | 1164 | const ptr_ty = cg.typeOfIndex(inst); |
| 1167 | 1165 | const pointee_ty = ptr_ty.childType(zcu); |
| 1168 | 1166 | |
| 1169 | | if (cg.initial_stack_value == .none) { |
| 1170 | | try cg.initializeStack(); |
| 1171 | | } |
| 1172 | | |
| 1173 | 1167 | const abi_alignment = ptr_ty.ptrAlignment(zcu); |
| 1174 | 1168 | const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse { |
| 1175 | 1169 | return cg.fail("Type {f} with ABI size of {d} exceeds stack frame size", .{ |
| ... | ... | @@ -1934,7 +1928,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 1934 | 1928 | continue; |
| 1935 | 1929 | } |
| 1936 | 1930 | const old_bookkeeping_value = cg.air_bookkeeping; |
| 1937 | | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, 1); |
| 1938 | 1931 | try cg.genInst(inst); |
| 1939 | 1932 | |
| 1940 | 1933 | if (std.debug.runtime_safety and cg.air_bookkeeping < old_bookkeeping_value + 1) { |
| ... | ... | @@ -2160,11 +2153,11 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 2160 | 2153 | .direct => |scalar_type| { |
| 2161 | 2154 | if (!isByRef(ret_ty, zcu, cg.target)) { |
| 2162 | 2155 | const result_local = try cg.allocLocal(ret_ty); |
| 2163 | | try cg.addLocal(.local_set, result_local.local.value); |
| 2156 | try cg.addLocal(.local_set, result_local.local); |
| 2164 | 2157 | break :result_value result_local; |
| 2165 | 2158 | } else { |
| 2166 | 2159 | const result_local = try cg.allocLocal(scalar_type); |
| 2167 | | try cg.addLocal(.local_set, result_local.local.value); |
| 2160 | try cg.addLocal(.local_set, result_local.local); |
| 2168 | 2161 | const result = try cg.allocStack(ret_ty); |
| 2169 | 2162 | try cg.store(result, result_local, scalar_type, .{}); |
| 2170 | 2163 | break :result_value result; |
| ... | ... | @@ -2175,7 +2168,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 2175 | 2168 | assert(vector.len == 1); |
| 2176 | 2169 | const result_local = try cg.allocLocal(vector.elem_type); |
| 2177 | 2170 | // save call result from operand stack |
| 2178 | | try cg.addLocal(.local_set, result_local.local.value); |
| 2171 | try cg.addLocal(.local_set, result_local.local); |
| 2179 | 2172 | const result = try cg.allocStack(ret_ty); |
| 2180 | 2173 | try cg.store(result, result_local, vector.elem_type, .{}); |
| 2181 | 2174 | break :result_value result; |
| ... | ... | @@ -2183,7 +2176,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 2183 | 2176 | } |
| 2184 | 2177 | } else { |
| 2185 | 2178 | const result_local = try cg.allocLocal(ret_ty); |
| 2186 | | try cg.addLocal(.local_set, result_local.local.value); |
| 2179 | try cg.addLocal(.local_set, result_local.local); |
| 2187 | 2180 | break :result_value result_local; |
| 2188 | 2181 | } |
| 2189 | 2182 | }; |
| ... | ... | @@ -2250,7 +2243,7 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2250 | 2243 | } |
| 2251 | 2244 | } |
| 2252 | 2245 | |
| 2253 | | try cg.addLocal(.local_set, arg_ptr.local.value); |
| 2246 | try cg.addLocal(.local_set, arg_ptr.local); |
| 2254 | 2247 | |
| 2255 | 2248 | try cg.emitWValue(operand); |
| 2256 | 2249 | try cg.emitWValue(arg_ptr); |
| ... | ... | @@ -2272,7 +2265,7 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2272 | 2265 | try cg.addTag(.f32_demote_f64); |
| 2273 | 2266 | |
| 2274 | 2267 | const result_local = try cg.allocLocal(Type.f32); |
| 2275 | | try cg.addLocal(.local_set, result_local.local.value); |
| 2268 | try cg.addLocal(.local_set, result_local.local); |
| 2276 | 2269 | break :result result_local; |
| 2277 | 2270 | } else try cg.load(arg_ptr, ty, .{}); |
| 2278 | 2271 | |
| ... | ... | @@ -3300,7 +3293,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3300 | 3293 | |
| 3301 | 3294 | var mask = try cg.allocLocal(Type.i32); |
| 3302 | 3295 | defer mask.free(cg); |
| 3303 | | try cg.addLocal(.local_tee, mask.local.value); |
| 3296 | try cg.addLocal(.local_tee, mask.local); |
| 3304 | 3297 | |
| 3305 | 3298 | try cg.emitWValue(operand); |
| 3306 | 3299 | try cg.addTag(.i32_xor); |
| ... | ... | @@ -3315,7 +3308,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3315 | 3308 | |
| 3316 | 3309 | var mask = try cg.allocLocal(Type.i64); |
| 3317 | 3310 | defer mask.free(cg); |
| 3318 | | try cg.addLocal(.local_tee, mask.local.value); |
| 3311 | try cg.addLocal(.local_tee, mask.local); |
| 3319 | 3312 | |
| 3320 | 3313 | try cg.emitWValue(operand); |
| 3321 | 3314 | try cg.addTag(.i64_xor); |
| ... | ... | @@ -3337,7 +3330,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3337 | 3330 | |
| 3338 | 3331 | var tmp = try cg.allocLocal(Type.u64); |
| 3339 | 3332 | defer tmp.free(cg); |
| 3340 | | try cg.addLocal(.local_tee, tmp.local.value); |
| 3333 | try cg.addLocal(.local_tee, tmp.local); |
| 3341 | 3334 | try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset }); |
| 3342 | 3335 | try cg.emitWValue(tmp); |
| 3343 | 3336 | try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset + 8 }); |
| ... | ... | @@ -4033,7 +4026,7 @@ fn toLocalInt(cg: *CodeGen, value: WValue, int_ty: IntType) InnerError!WValue { |
| 4033 | 4026 | else => return cg.fail("TODO: Support toLocalInt for integer bitsize: {d}", .{int_ty.bits}), |
| 4034 | 4027 | }; |
| 4035 | 4028 | const new_local = try cg.allocLocal(ty); |
| 4036 | | try cg.addLocal(.local_set, new_local.local.value); |
| 4029 | try cg.addLocal(.local_set, new_local.local); |
| 4037 | 4030 | return new_local; |
| 4038 | 4031 | }, |
| 4039 | 4032 | .local, .stack_offset => return value, |
| ... | ... | @@ -4112,7 +4105,7 @@ fn intSubOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro |
| 4112 | 4105 | fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult { |
| 4113 | 4106 | const overflow_bit = try cg.allocLocal(Type.u32); |
| 4114 | 4107 | try cg.addImm32(0); |
| 4115 | | try cg.addLocal(.local_set, overflow_bit.local.value); |
| 4108 | try cg.addLocal(.local_set, overflow_bit.local); |
| 4116 | 4109 | |
| 4117 | 4110 | const result_val = if (int_ty.bits <= 32) blk: { |
| 4118 | 4111 | const new_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = 64 }; |
| ... | ... | @@ -4126,7 +4119,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner |
| 4126 | 4119 | |
| 4127 | 4120 | const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp); |
| 4128 | 4121 | _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op); |
| 4129 | | try cg.addLocal(.local_set, overflow_bit.local.value); |
| 4122 | try cg.addLocal(.local_set, overflow_bit.local); |
| 4130 | 4123 | break :blk res_tmp; |
| 4131 | 4124 | } else if (int_ty.bits <= 64) blk: { |
| 4132 | 4125 | const new_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = 128 }; |
| ... | ... | @@ -4140,7 +4133,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner |
| 4140 | 4133 | |
| 4141 | 4134 | const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp); |
| 4142 | 4135 | _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op); |
| 4143 | | try cg.addLocal(.local_set, overflow_bit.local.value); |
| 4136 | try cg.addLocal(.local_set, overflow_bit.local); |
| 4144 | 4137 | break :blk res_tmp; |
| 4145 | 4138 | } else if (int_ty.bits == 128 and int_ty.is_signed) blk: { |
| 4146 | 4139 | const overflow_ret = try cg.allocStack(Type.i32); |
| ... | ... | @@ -4151,7 +4144,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner |
| 4151 | 4144 | &.{ lhs, rhs, overflow_ret }, |
| 4152 | 4145 | ); |
| 4153 | 4146 | _ = try cg.load(overflow_ret, .i32, .{}); |
| 4154 | | try cg.addLocal(.local_set, overflow_bit.local.value); |
| 4147 | try cg.addLocal(.local_set, overflow_bit.local); |
| 4155 | 4148 | break :blk res; |
| 4156 | 4149 | } else { |
| 4157 | 4150 | const result = try cg.allocInt(int_ty); |
| ... | ... | @@ -5232,17 +5225,17 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const |
| 5232 | 5225 | .label = cg.block_depth, |
| 5233 | 5226 | .value = block_result, |
| 5234 | 5227 | }); |
| 5228 | defer assert(cg.blocks.swapRemove(inst)); |
| 5235 | 5229 | |
| 5236 | 5230 | { |
| 5237 | | try cg.branches.append(cg.gpa, .{}); |
| 5238 | | defer { |
| 5239 | | var branch = cg.branches.pop().?; |
| 5240 | | branch.deinit(cg.gpa); |
| 5241 | | } |
| 5231 | try cg.pushBranch(); |
| 5232 | defer cg.popBranch(); |
| 5242 | 5233 | try cg.genBody(body); |
| 5243 | 5234 | try cg.endBlock(); |
| 5244 | 5235 | } |
| 5245 | 5236 | |
| 5237 | for (cg.liveness.getBlock(inst).deaths) |death| cg.processDeath(death.toRef()); |
| 5238 | |
| 5246 | 5239 | return cg.finishAir(inst, block_result, &.{}); |
| 5247 | 5240 | } |
| 5248 | 5241 | |
| ... | ... | @@ -5282,6 +5275,7 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5282 | 5275 | const condition = try cg.resolveInst(cond_br.condition); |
| 5283 | 5276 | const then_body = cond_br.then_body; |
| 5284 | 5277 | const else_body = cond_br.else_body; |
| 5278 | const branch_liveness = cg.liveness.getCondBr(inst); |
| 5285 | 5279 | |
| 5286 | 5280 | // result type is always noreturn, so use `block_empty` as type. |
| 5287 | 5281 | try cg.startBlock(.block, .empty); |
| ... | ... | @@ -5293,24 +5287,21 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5293 | 5287 | // and continue with the then codepath |
| 5294 | 5288 | try cg.addLabel(.br_if, 0); |
| 5295 | 5289 | |
| 5296 | | try cg.branches.ensureUnusedCapacity(cg.gpa, 2); |
| 5290 | if (cg.liveness.operandDies(inst, 0)) cg.processDeath(cond_br.condition); |
| 5291 | |
| 5297 | 5292 | { |
| 5298 | | cg.branches.appendAssumeCapacity(.{}); |
| 5299 | | defer { |
| 5300 | | var else_stack = cg.branches.pop().?; |
| 5301 | | else_stack.deinit(cg.gpa); |
| 5302 | | } |
| 5293 | try cg.pushBranch(); |
| 5294 | defer cg.popBranch(); |
| 5295 | for (branch_liveness.else_deaths) |death| cg.processDeath(death.toRef()); |
| 5303 | 5296 | try cg.genBody(else_body); |
| 5304 | 5297 | try cg.endBlock(); |
| 5305 | 5298 | } |
| 5306 | 5299 | |
| 5307 | 5300 | // Outer block that matches the condition |
| 5308 | 5301 | { |
| 5309 | | cg.branches.appendAssumeCapacity(.{}); |
| 5310 | | defer { |
| 5311 | | var then_stack = cg.branches.pop().?; |
| 5312 | | then_stack.deinit(cg.gpa); |
| 5313 | | } |
| 5302 | try cg.pushBranch(); |
| 5303 | defer cg.popBranch(); |
| 5304 | for (branch_liveness.then_deaths) |death| cg.processDeath(death.toRef()); |
| 5314 | 5305 | try cg.genBody(then_body); |
| 5315 | 5306 | } |
| 5316 | 5307 | |
| ... | ... | @@ -5346,18 +5337,18 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne |
| 5346 | 5337 | try cg.startBlock(.block, .empty); |
| 5347 | 5338 | |
| 5348 | 5339 | try cg.addImm32(if (op == .eq) 0 else 1); |
| 5349 | | try cg.addLocal(.local_set, result.local.value); |
| 5340 | try cg.addLocal(.local_set, result.local); |
| 5350 | 5341 | |
| 5351 | 5342 | _ = try cg.isNull(lhs, operand_ty, .i32_eq, .value); |
| 5352 | | try cg.addLocal(.local_tee, lhs_null.local.value); |
| 5343 | try cg.addLocal(.local_tee, lhs_null.local); |
| 5353 | 5344 | _ = try cg.isNull(rhs, operand_ty, .i32_eq, .value); |
| 5354 | 5345 | try cg.addTag(.i32_ne); |
| 5355 | 5346 | try cg.addLabel(.br_if, 0); |
| 5356 | 5347 | |
| 5357 | 5348 | try cg.addImm32(if (op == .eq) 1 else 0); |
| 5358 | | try cg.addLocal(.local_set, result.local.value); |
| 5349 | try cg.addLocal(.local_set, result.local); |
| 5359 | 5350 | |
| 5360 | | try cg.addLocal(.local_get, lhs_null.local.value); |
| 5351 | try cg.addLocal(.local_get, lhs_null.local); |
| 5361 | 5352 | try cg.addLabel(.br_if, 0); |
| 5362 | 5353 | |
| 5363 | 5354 | _ = try cg.load(lhs, payload_ty, .{}); |
| ... | ... | @@ -5369,10 +5360,10 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne |
| 5369 | 5360 | _ = try cg.intCmp(.fromType(cg, payload_ty), op, .stack, .stack); |
| 5370 | 5361 | } |
| 5371 | 5362 | |
| 5372 | | try cg.addLocal(.local_set, result.local.value); |
| 5363 | try cg.addLocal(.local_set, result.local); |
| 5373 | 5364 | try cg.endBlock(); |
| 5374 | 5365 | |
| 5375 | | try cg.addLocal(.local_get, result.local.value); |
| 5366 | try cg.addLocal(.local_get, result.local); |
| 5376 | 5367 | try cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); |
| 5377 | 5368 | } else { |
| 5378 | 5369 | const result = try cg.intCmp(.fromType(cg, operand_ty), op, lhs, rhs); |
| ... | ... | @@ -5570,7 +5561,7 @@ fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5570 | 5561 | if (block.value != .none) { |
| 5571 | 5562 | const operand = try cg.resolveInst(br.operand); |
| 5572 | 5563 | try cg.emitWValue(operand); |
| 5573 | | try cg.addLocal(.local_set, block.value.local.value); |
| 5564 | try cg.addLocal(.local_set, block.value.local); |
| 5574 | 5565 | } |
| 5575 | 5566 | |
| 5576 | 5567 | // We map every block to its block index. |
| ... | ... | @@ -5854,24 +5845,29 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner |
| 5854 | 5845 | |
| 5855 | 5846 | const switch_br = cg.air.unwrapSwitch(inst); |
| 5856 | 5847 | const target_ty = cg.typeOf(switch_br.operand); |
| 5848 | const branch_liveness = try cg.liveness.getSwitchBr(cg.gpa, inst, switch_br.cases_len + 1); |
| 5849 | defer cg.gpa.free(branch_liveness.deaths); |
| 5857 | 5850 | |
| 5858 | 5851 | assert(target_ty.hasRuntimeBits(zcu)); |
| 5859 | 5852 | |
| 5860 | 5853 | // swap target value with placeholder local, for dispatching |
| 5854 | var owned_target: WValue = .none; |
| 5855 | defer owned_target.free(cg); |
| 5861 | 5856 | const target = if (is_dispatch_loop) target: { |
| 5862 | 5857 | const initial_target = try cg.resolveInst(switch_br.operand); |
| 5863 | | const target: WValue = try cg.allocLocal(target_ty); |
| 5858 | owned_target = try cg.allocLocal(target_ty); |
| 5864 | 5859 | try cg.emitWValue(initial_target); |
| 5865 | | try cg.addLocal(.local_set, target.local.value); |
| 5860 | try cg.addLocal(.local_set, owned_target.local); |
| 5866 | 5861 | |
| 5867 | 5862 | try cg.startBlock(.loop, .empty); // dispatch loop start |
| 5868 | 5863 | try cg.blocks.putNoClobber(cg.gpa, inst, .{ |
| 5869 | 5864 | .label = cg.block_depth, |
| 5870 | | .value = target, |
| 5865 | .value = owned_target, |
| 5871 | 5866 | }); |
| 5872 | 5867 | |
| 5873 | | break :target target; |
| 5868 | break :target owned_target; |
| 5874 | 5869 | } else try cg.resolveInst(switch_br.operand); |
| 5870 | defer if (is_dispatch_loop) assert(cg.blocks.swapRemove(inst)); |
| 5875 | 5871 | |
| 5876 | 5872 | const has_else_body = switch_br.else_body_len != 0; |
| 5877 | 5873 | const branch_count = switch_br.cases_len + 1; // if else branch is missing, we trap when failing all conditions |
| ... | ... | @@ -5883,12 +5879,14 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner |
| 5883 | 5879 | var it = switch_br.iterateCases(); |
| 5884 | 5880 | const else_body = it.elseBody(); |
| 5885 | 5881 | |
| 5886 | | cg.branches.appendAssumeCapacity(.{}); |
| 5887 | | defer { |
| 5888 | | var else_branch = cg.branches.pop().?; |
| 5889 | | else_branch.deinit(cg.gpa); |
| 5882 | if (cg.liveness.operandDies(inst, 0)) cg.processDeath(switch_br.operand); |
| 5883 | |
| 5884 | { |
| 5885 | try cg.pushBranch(); |
| 5886 | defer cg.popBranch(); |
| 5887 | for (branch_liveness.deaths[0]) |death| cg.processDeath(death.toRef()); |
| 5888 | try cg.genBody(else_body); |
| 5890 | 5889 | } |
| 5891 | | try cg.genBody(else_body); |
| 5892 | 5890 | |
| 5893 | 5891 | if (is_dispatch_loop) { |
| 5894 | 5892 | try cg.endBlock(); // dispatch loop end |
| ... | ... | @@ -6012,16 +6010,18 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner |
| 6012 | 6010 | try cg.addLabel(.br, branch_count - 1); |
| 6013 | 6011 | } |
| 6014 | 6012 | |
| 6013 | if (cg.liveness.operandDies(inst, 0)) cg.processDeath(switch_br.operand); |
| 6014 | |
| 6015 | 6015 | var cases_it = switch_br.iterateCases(); |
| 6016 | 6016 | while (cases_it.next()) |case| { |
| 6017 | 6017 | try cg.endBlock(); |
| 6018 | 6018 | |
| 6019 | | cg.branches.appendAssumeCapacity(.{}); |
| 6020 | | defer { |
| 6021 | | var case_branch = cg.branches.pop().?; |
| 6022 | | case_branch.deinit(cg.gpa); |
| 6019 | { |
| 6020 | try cg.pushBranch(); |
| 6021 | defer cg.popBranch(); |
| 6022 | for (branch_liveness.deaths[case.idx]) |death| cg.processDeath(death.toRef()); |
| 6023 | try cg.genBody(case.body); |
| 6023 | 6024 | } |
| 6024 | | try cg.genBody(case.body); |
| 6025 | 6025 | |
| 6026 | 6026 | try cg.addLabel(.br, branch_count - case.idx - 1); // matching case found and executed => exit switch |
| 6027 | 6027 | } |
| ... | ... | @@ -6030,12 +6030,12 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner |
| 6030 | 6030 | if (has_else_body) { |
| 6031 | 6031 | const else_body = cases_it.elseBody(); |
| 6032 | 6032 | |
| 6033 | | cg.branches.appendAssumeCapacity(.{}); |
| 6034 | | defer { |
| 6035 | | var else_branch = cg.branches.pop().?; |
| 6036 | | else_branch.deinit(cg.gpa); |
| 6033 | { |
| 6034 | try cg.pushBranch(); |
| 6035 | defer cg.popBranch(); |
| 6036 | for (branch_liveness.deaths[branch_liveness.deaths.len - 1]) |death| cg.processDeath(death.toRef()); |
| 6037 | try cg.genBody(else_body); |
| 6037 | 6038 | } |
| 6038 | | try cg.genBody(else_body); |
| 6039 | 6039 | } else { |
| 6040 | 6040 | try cg.addTag(.@"unreachable"); |
| 6041 | 6041 | } |
| ... | ... | @@ -6055,7 +6055,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6055 | 6055 | |
| 6056 | 6056 | const operand = try cg.resolveInst(br.operand); |
| 6057 | 6057 | try cg.emitWValue(operand); |
| 6058 | | try cg.addLocal(.local_set, switch_loop.value.local.value); |
| 6058 | try cg.addLocal(.local_set, switch_loop.value.local); |
| 6059 | 6059 | |
| 6060 | 6060 | const idx: u32 = cg.block_depth - switch_loop.label; |
| 6061 | 6061 | try cg.addLabel(.br, idx); |
| ... | ... | @@ -6630,7 +6630,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) |
| 6630 | 6630 | try cg.addTag(.i64_mul); |
| 6631 | 6631 | }, |
| 6632 | 6632 | } |
| 6633 | | try cg.addLocal(.local_set, new_len.local.value); |
| 6633 | try cg.addLocal(.local_set, new_len.local); |
| 6634 | 6634 | break :blk new_len; |
| 6635 | 6635 | } else len, |
| 6636 | 6636 | }; |
| ... | ... | @@ -6640,7 +6640,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) |
| 6640 | 6640 | var new_ptr = try cg.allocLocal(Type.usize); |
| 6641 | 6641 | defer new_ptr.free(cg); |
| 6642 | 6642 | try cg.emitWValue(ptr); |
| 6643 | | try cg.addLocal(.local_set, new_ptr.local.value); |
| 6643 | try cg.addLocal(.local_set, new_ptr.local); |
| 6644 | 6644 | |
| 6645 | 6645 | // get the loop conditional: if current pointer address equals final pointer's address |
| 6646 | 6646 | try cg.emitWValue(ptr); |
| ... | ... | @@ -6649,7 +6649,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) |
| 6649 | 6649 | .wasm32 => try cg.addTag(.i32_add), |
| 6650 | 6650 | .wasm64 => try cg.addTag(.i64_add), |
| 6651 | 6651 | } |
| 6652 | | try cg.addLocal(.local_set, end_ptr.local.value); |
| 6652 | try cg.addLocal(.local_set, end_ptr.local); |
| 6653 | 6653 | |
| 6654 | 6654 | // outer block to jump to when loop is done |
| 6655 | 6655 | try cg.startBlock(.block, .empty); |
| ... | ... | @@ -6679,7 +6679,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) |
| 6679 | 6679 | try cg.addTag(.i64_add); |
| 6680 | 6680 | }, |
| 6681 | 6681 | } |
| 6682 | | try cg.addLocal(.local_set, new_ptr.local.value); |
| 6682 | try cg.addLocal(.local_set, new_ptr.local); |
| 6683 | 6683 | |
| 6684 | 6684 | // end of loop |
| 6685 | 6685 | try cg.addLabel(.br, 0); // jump to start of loop |
| ... | ... | @@ -7186,7 +7186,7 @@ fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7186 | 7186 | try cg.emitWValue(slice_len); |
| 7187 | 7187 | try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) }); |
| 7188 | 7188 | try cg.addTag(.i32_mul); |
| 7189 | | try cg.addLocal(.local_set, slice_len.local.value); |
| 7189 | try cg.addLocal(.local_set, slice_len.local); |
| 7190 | 7190 | } |
| 7191 | 7191 | break :blk slice_len; |
| 7192 | 7192 | }, |
| ... | ... | @@ -7217,7 +7217,7 @@ fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7217 | 7217 | try cg.emitWValue(slice_len); |
| 7218 | 7218 | try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) }); |
| 7219 | 7219 | try cg.addTag(.i32_mul); |
| 7220 | | try cg.addLocal(.local_set, slice_len.local.value); |
| 7220 | try cg.addLocal(.local_set, slice_len.local); |
| 7221 | 7221 | } |
| 7222 | 7222 | break :blk slice_len; |
| 7223 | 7223 | }, |
| ... | ... | @@ -7303,8 +7303,13 @@ fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7303 | 7303 | const body = unwrapped_try.else_body; |
| 7304 | 7304 | const err_union = try cg.resolveInst(unwrapped_try.error_union); |
| 7305 | 7305 | const err_union_ty = cg.typeOf(unwrapped_try.error_union); |
| 7306 | | const result = try lowerTry(cg, inst, err_union, body, err_union_ty, false); |
| 7307 | | return cg.finishAir(inst, result, &.{unwrapped_try.error_union}); |
| 7306 | const result = try lowerTry(cg, inst, unwrapped_try.error_union, err_union, body, err_union_ty, false); |
| 7307 | return cg.finishAirWithBranchDeaths( |
| 7308 | inst, |
| 7309 | result, |
| 7310 | &.{unwrapped_try.error_union}, |
| 7311 | cg.liveness.getCondBr(inst).then_deaths, |
| 7312 | ); |
| 7308 | 7313 | } |
| 7309 | 7314 | |
| 7310 | 7315 | fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -7313,20 +7318,26 @@ fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7313 | 7318 | const err_union_ptr = try cg.resolveInst(unwrapped_try.error_union_ptr); |
| 7314 | 7319 | const body = unwrapped_try.else_body; |
| 7315 | 7320 | const err_union_ty = cg.typeOf(unwrapped_try.error_union_ptr).childType(zcu); |
| 7316 | | const result = try lowerTry(cg, inst, err_union_ptr, body, err_union_ty, true); |
| 7317 | | return cg.finishAir(inst, result, &.{unwrapped_try.error_union_ptr}); |
| 7321 | const result = try lowerTry(cg, inst, unwrapped_try.error_union_ptr, err_union_ptr, body, err_union_ty, true); |
| 7322 | return cg.finishAirWithBranchDeaths( |
| 7323 | inst, |
| 7324 | result, |
| 7325 | &.{unwrapped_try.error_union_ptr}, |
| 7326 | cg.liveness.getCondBr(inst).then_deaths, |
| 7327 | ); |
| 7318 | 7328 | } |
| 7319 | 7329 | |
| 7320 | 7330 | fn lowerTry( |
| 7321 | 7331 | cg: *CodeGen, |
| 7322 | 7332 | inst: Air.Inst.Index, |
| 7333 | operand_ref: Air.Inst.Ref, |
| 7323 | 7334 | err_union: WValue, |
| 7324 | 7335 | body: []const Air.Inst.Index, |
| 7325 | 7336 | err_union_ty: Type, |
| 7326 | 7337 | operand_is_ptr: bool, |
| 7327 | 7338 | ) InnerError!WValue { |
| 7328 | | _ = inst; |
| 7329 | 7339 | const zcu = cg.pt.zcu; |
| 7340 | const branch_liveness = cg.liveness.getCondBr(inst); |
| 7330 | 7341 | |
| 7331 | 7342 | const pl_ty = err_union_ty.errorUnionPayload(zcu); |
| 7332 | 7343 | const pl_has_bits = pl_ty.hasRuntimeBits(zcu); |
| ... | ... | @@ -7349,12 +7360,13 @@ fn lowerTry( |
| 7349 | 7360 | try cg.addTag(.i32_eqz); |
| 7350 | 7361 | try cg.addLabel(.br_if, 0); // jump out of block when error is '0' |
| 7351 | 7362 | |
| 7352 | | try cg.branches.append(cg.gpa, .{}); |
| 7353 | | defer { |
| 7354 | | var branch = cg.branches.pop().?; |
| 7355 | | branch.deinit(cg.gpa); |
| 7363 | { |
| 7364 | try cg.pushBranch(); |
| 7365 | defer cg.popBranch(); |
| 7366 | if (cg.liveness.operandDies(inst, 0)) cg.processDeath(operand_ref); |
| 7367 | for (branch_liveness.else_deaths) |death| cg.processDeath(death.toRef()); |
| 7368 | try cg.genBody(body); |
| 7356 | 7369 | } |
| 7357 | | try cg.genBody(body); |
| 7358 | 7370 | try cg.endBlock(); |
| 7359 | 7371 | } |
| 7360 | 7372 | |
| ... | ... | @@ -7367,8 +7379,7 @@ fn lowerTry( |
| 7367 | 7379 | if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) { |
| 7368 | 7380 | return cg.offsetPointer(err_union, pl_offset); |
| 7369 | 7381 | } |
| 7370 | | const payload = try cg.load(err_union, pl_ty, .{ .offset = pl_offset }); |
| 7371 | | return payload.toLocal(cg, pl_ty); |
| 7382 | return cg.load(err_union, pl_ty, .{ .offset = pl_offset }); |
| 7372 | 7383 | } |
| 7373 | 7384 | |
| 7374 | 7385 | /// Calls a compiler-rt intrinsic by creating an undefined symbol, |
| ... | ... | @@ -7512,13 +7523,13 @@ fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7512 | 7523 | // 'false' branch (i.e. error set does not have value |
| 7513 | 7524 | // ensure we set local to 0 in case the local was re-used. |
| 7514 | 7525 | try cg.addImm32(0); |
| 7515 | | try cg.addLocal(.local_set, result.local.value); |
| 7526 | try cg.addLocal(.local_set, result.local); |
| 7516 | 7527 | try cg.addLabel(.br, 1); |
| 7517 | 7528 | try cg.endBlock(); |
| 7518 | 7529 | |
| 7519 | 7530 | // 'true' branch |
| 7520 | 7531 | try cg.addImm32(1); |
| 7521 | | try cg.addLocal(.local_set, result.local.value); |
| 7532 | try cg.addLocal(.local_set, result.local); |
| 7522 | 7533 | try cg.addLabel(.br, 0); |
| 7523 | 7534 | try cg.endBlock(); |
| 7524 | 7535 | |
| ... | ... | @@ -7561,9 +7572,9 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7561 | 7572 | .offset = ptr_operand.memOffset(), |
| 7562 | 7573 | .alignment = ty.abiAlignment(zcu).toStdMem(), |
| 7563 | 7574 | }); |
| 7564 | | try cg.addLocal(.local_tee, val_local.local.value); |
| 7575 | try cg.addLocal(.local_tee, val_local.local); |
| 7565 | 7576 | _ = try cg.intCmp(int_ty, .eq, .stack, expected_val); |
| 7566 | | try cg.addLocal(.local_set, cmp_result.local.value); |
| 7577 | try cg.addLocal(.local_set, cmp_result.local); |
| 7567 | 7578 | break :val val_local; |
| 7568 | 7579 | } else val: { |
| 7569 | 7580 | if (ty.abiSize(zcu) > 8) { |
| ... | ... | @@ -7575,7 +7586,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7575 | 7586 | try cg.emitWValue(new_val); |
| 7576 | 7587 | try cg.emitWValue(ptr_val); |
| 7577 | 7588 | _ = try cg.intCmp(int_ty, .eq, ptr_val, expected_val); |
| 7578 | | try cg.addLocal(.local_tee, cmp_result.local.value); |
| 7589 | try cg.addLocal(.local_tee, cmp_result.local); |
| 7579 | 7590 | try cg.addTag(.select); |
| 7580 | 7591 | try cg.store(.stack, .stack, ty, .{ .offset = ptr_operand.memOffset() }); |
| 7581 | 7592 | |
| ... | ... | @@ -7685,11 +7696,11 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7685 | 7696 | }, |
| 7686 | 7697 | ); |
| 7687 | 7698 | const select_res = try cg.allocLocal(ty); |
| 7688 | | try cg.addLocal(.local_tee, select_res.local.value); |
| 7699 | try cg.addLocal(.local_tee, select_res.local); |
| 7689 | 7700 | _ = try cg.intCmp(int_ty, .neq, .stack, value); // leave on stack so we can use it for br_if |
| 7690 | 7701 | |
| 7691 | 7702 | try cg.emitWValue(select_res); |
| 7692 | | try cg.addLocal(.local_set, value.local.value); |
| 7703 | try cg.addLocal(.local_set, value.local); |
| 7693 | 7704 | |
| 7694 | 7705 | try cg.addLabel(.br_if, 0); |
| 7695 | 7706 | try cg.endBlock(); |
| ... | ... | @@ -7864,9 +7875,6 @@ fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7864 | 7875 | } |
| 7865 | 7876 | |
| 7866 | 7877 | fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7867 | | if (cg.initial_stack_value == .none) { |
| 7868 | | try cg.initializeStack(); |
| 7869 | | } |
| 7870 | 7878 | try cg.emitWValue(cg.bottom_stack_value); |
| 7871 | 7879 | return cg.finishAir(inst, .stack, &.{}); |
| 7872 | 7880 | } |
| ... | ... | @@ -7912,7 +7920,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7912 | 7920 | return cg.fail("Self-hosted wasm backend requires output constraint to be equal \"=r\"", .{}); |
| 7913 | 7921 | } |
| 7914 | 7922 | |
| 7915 | | const gop = try local_map.getOrPutValue(cg.gpa, name, result.local.value); |
| 7923 | const gop = try local_map.getOrPutValue(cg.gpa, name, result.local); |
| 7916 | 7924 | assert(!gop.found_existing); // first value |
| 7917 | 7925 | |
| 7918 | 7926 | assert(it.next() == null); |
| ... | ... | @@ -7933,7 +7941,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7933 | 7941 | try cg.emitWValue(operand); |
| 7934 | 7942 | const op_local = try WValue.toLocal(.stack, cg, cg.typeOf(input.operand)); |
| 7935 | 7943 | |
| 7936 | | const gop = try local_map.getOrPutValue(cg.gpa, name, op_local.local.value); |
| 7944 | const gop = try local_map.getOrPutValue(cg.gpa, name, op_local.local); |
| 7937 | 7945 | if (gop.found_existing) { |
| 7938 | 7946 | return cg.fail("Duplicate asm variable name \"{s}\"", .{name}); |
| 7939 | 7947 | } |