| ... | @@ -136,13 +136,10 @@ air: Air, | ... | @@ -136,13 +136,10 @@ air: Air, |
| 136 | liveness: Air.Liveness, | 136 | liveness: Air.Liveness, |
| 137 | gpa: mem.Allocator, | 137 | gpa: mem.Allocator, |
| 138 | func_index: InternPool.Index, | 138 | func_index: InternPool.Index, |
| 139 | /// Contains a list of current branches. | 139 | /// Contains a list of current branches. Stores LocalPool before entering branch. |
| 140 | /// When we return from a branch, the branch will be popped from this list, | 140 | branches: std.ArrayList(LocalPool) = .empty, |
| 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, | | |
| 144 | /// Table to save `WValue`'s generated by an `Air.Inst` | 141 | /// Table to save `WValue`'s generated by an `Air.Inst` |
| 145 | // values: ValueTable, | 142 | values: ValueTable = .empty, |
| 146 | /// Mapping from Air.Inst.Index to block ids | 143 | /// Mapping from Air.Inst.Index to block ids |
| 147 | blocks: std.array_hash_map.Auto(Air.Inst.Index, struct { | 144 | blocks: std.array_hash_map.Auto(Air.Inst.Index, struct { |
| 148 | label: u32, | 145 | label: u32, |
| ... | @@ -150,10 +147,6 @@ blocks: std.array_hash_map.Auto(Air.Inst.Index, struct { | ... | @@ -150,10 +147,6 @@ blocks: std.array_hash_map.Auto(Air.Inst.Index, struct { |
| 150 | }) = .{}, | 147 | }) = .{}, |
| 151 | /// Maps `loop` instructions to their label. `br` to here repeats the loop. | 148 | /// Maps `loop` instructions to their label. `br` to here repeats the loop. |
| 152 | loops: std.AutoHashMapUnmanaged(Air.Inst.Index, u32) = .empty, | 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 | /// The index of the current argument. | 150 | /// The index of the current argument. |
| 158 | /// Used to track which argument is being referenced in `airArg`. | 151 | /// Used to track which argument is being referenced in `airArg`. |
| 159 | arg_index: u32 = 0, | 152 | arg_index: u32 = 0, |
| ... | @@ -207,23 +200,8 @@ stack_size: u32 = 0, | ... | @@ -207,23 +200,8 @@ stack_size: u32 = 0, |
| 207 | /// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default. | 200 | /// However, local variables or the usage of `incoming_stack_alignment` in a `CallingConvention` can overwrite this default. |
| 208 | stack_alignment: Alignment = .@"16", | 201 | stack_alignment: Alignment = .@"16", |
| 209 | | 202 | |
| 210 | // For each individual Wasm valtype we store a seperate free list which | 203 | local_pool: LocalPool = .{}, |
| 211 | // allows us to re-use locals that are no longer used. e.g. a temporary local. | 204 | wasm_param_count: u32 = undefined, |
| 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, | | |
| 227 | | 205 | |
| 228 | /// When in debug mode, this tracks if no `finishAir` was missed. | 206 | /// When in debug mode, this tracks if no `finishAir` was missed. |
| 229 | /// Forgetting to call `finishAir` will cause the result to not be | 207 | /// Forgetting to call `finishAir` will cause the result to not be |
| ... | @@ -239,13 +217,7 @@ const WValue = union(enum) { | ... | @@ -239,13 +217,7 @@ const WValue = union(enum) { |
| 239 | none: void, | 217 | none: void, |
| 240 | /// The value lives on top of the stack | 218 | /// The value lives on top of the stack |
| 241 | stack: void, | 219 | stack: void, |
| 242 | /// Index of the local | 220 | local: u32, // index |
| 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 | }, | | |
| 249 | /// An immediate 32bit value | 221 | /// An immediate 32bit value |
| 250 | imm32: u32, | 222 | imm32: u32, |
| 251 | /// An immediate 64bit value | 223 | /// An immediate 64bit value |
| ... | @@ -290,7 +262,7 @@ const WValue = union(enum) { | ... | @@ -290,7 +262,7 @@ const WValue = union(enum) { |
| 290 | switch (value) { | 262 | switch (value) { |
| 291 | .stack => { | 263 | .stack => { |
| 292 | const new_local = try gen.allocLocal(ty); | 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 | return new_local; | 266 | return new_local; |
| 295 | }, | 267 | }, |
| 296 | else => return value, | 268 | else => return value, |
| ... | @@ -299,23 +271,9 @@ const WValue = union(enum) { | ... | @@ -299,23 +271,9 @@ const WValue = union(enum) { |
| 299 | | 271 | |
| 300 | /// Marks a local as no longer being referenced and essentially allows | 272 | /// Marks a local as no longer being referenced and essentially allows |
| 301 | /// us to re-use it somewhere else within the function. | 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`. | 274 | fn free(value: *WValue, cg: *CodeGen) void { |
| 303 | fn free(value: *WValue, gen: *CodeGen) void { | | |
| 304 | if (value.* != .local) return; | 275 | if (value.* != .local) return; |
| 305 | const local_value = value.local.value; | 276 | cg.releaseLocal(value.local); |
| 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 }); | | |
| 319 | value.* = .dead; | 277 | value.* = .dead; |
| 320 | } | 278 | } |
| 321 | }; | 279 | }; |
| ... | @@ -323,6 +281,77 @@ const WValue = union(enum) { | ... | @@ -323,6 +281,77 @@ const WValue = union(enum) { |
| 323 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` | 281 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` |
| 324 | const ValueTable = std.array_hash_map.Auto(Air.Inst.Ref, WValue); | 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 | const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; | 355 | const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 327 | | 356 | |
| 328 | const InnerError = Error || error{ | 357 | const InnerError = Error || error{ |
| ... | @@ -336,14 +365,11 @@ pub fn deinit(cg: *CodeGen) void { | ... | @@ -336,14 +365,11 @@ pub fn deinit(cg: *CodeGen) void { |
| 336 | const gpa = cg.gpa; | 365 | const gpa = cg.gpa; |
| 337 | for (cg.branches.items) |*branch| branch.deinit(gpa); | 366 | for (cg.branches.items) |*branch| branch.deinit(gpa); |
| 338 | cg.branches.deinit(gpa); | 367 | cg.branches.deinit(gpa); |
| | 368 | cg.values.deinit(gpa); |
| 339 | cg.blocks.deinit(gpa); | 369 | cg.blocks.deinit(gpa); |
| 340 | cg.loops.deinit(gpa); | 370 | cg.loops.deinit(gpa); |
| 341 | cg.simd_immediates.deinit(gpa); | 371 | cg.simd_immediates.deinit(gpa); |
| 342 | cg.free_locals_i32.deinit(gpa); | 372 | cg.local_pool.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); | | |
| 347 | cg.mir_instructions.deinit(gpa); | 373 | cg.mir_instructions.deinit(gpa); |
| 348 | cg.mir_extra.deinit(gpa); | 374 | cg.mir_extra.deinit(gpa); |
| 349 | cg.mir_locals.deinit(gpa); | 375 | cg.mir_locals.deinit(gpa); |
| ... | @@ -362,19 +388,11 @@ pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) Error { | ... | @@ -362,19 +388,11 @@ pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) Error { |
| 362 | /// Resolves the `WValue` for the given instruction `inst` | 388 | /// Resolves the `WValue` for the given instruction `inst` |
| 363 | /// When the given instruction has a `Value`, it returns a constant instead | 389 | /// When the given instruction has a `Value`, it returns a constant instead |
| 364 | fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { | 390 | fn resolveInst(cg: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { |
| 365 | var branch_index = cg.branches.items.len; | 391 | if (cg.values.get(ref)) |value| return value; |
| 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 | } | | |
| 372 | | 392 | |
| 373 | // when we did not find an existing instruction, it | 393 | // when we did not find an existing instruction, it |
| 374 | // means we must generate it from a constant. | 394 | // means we must generate it from a constant. |
| 375 | // We always store constants in the most outer branch as they must never | 395 | const gop = try cg.values.getOrPut(cg.gpa, ref); |
| 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); | | |
| 378 | assert(!gop.found_existing); | 396 | assert(!gop.found_existing); |
| 379 | | 397 | |
| 380 | const pt = cg.pt; | 398 | const pt = cg.pt; |
| ... | @@ -413,20 +431,33 @@ fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue { | ... | @@ -413,20 +431,33 @@ fn resolveValue(cg: *CodeGen, val: Value) InnerError!WValue { |
| 413 | | 431 | |
| 414 | /// NOTE: if result == .stack, it will be stored in .local | 432 | /// NOTE: if result == .stack, it will be stored in .local |
| 415 | fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []const Air.Inst.Ref) InnerError!void { | 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 | assert(operands.len <= Air.Liveness.bpi - 1); | 445 | assert(operands.len <= Air.Liveness.bpi - 1); |
| 417 | | 446 | |
| 418 | var final_result = result; | 447 | if (result == .local) aliased: { |
| 419 | const aliased_operand: ?*WValue = if (result == .local) aliased: { | | |
| 420 | for (operands) |operand| { | 448 | for (operands) |operand| { |
| 421 | if (operand.toIndex() == null) continue; | 449 | if (operand.toIndex() == null) continue; |
| 422 | const resolved_operand = cg.getResolvedInst(operand); | 450 | const resolved_operand = cg.values.getPtr(operand) orelse unreachable; |
| 423 | if (resolved_operand.* != .local or resolved_operand.local.value != result.local.value) continue; | 451 | if (resolved_operand.* != .local or resolved_operand.local != result.local) continue; |
| 424 | | 452 | |
| 425 | resolved_operand.local.references += 1; | 453 | if (result.local >= cg.wasm_param_count) { |
| 426 | break :aliased resolved_operand; | 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; | 460 | } |
| 429 | } else null; | | |
| 430 | | 461 | |
| 431 | var tomb_bits = cg.liveness.getTombBits(inst); | 462 | var tomb_bits = cg.liveness.getTombBits(inst); |
| 432 | for (operands) |operand| { | 463 | for (operands) |operand| { |
| ... | @@ -435,8 +466,8 @@ fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []con | ... | @@ -435,8 +466,8 @@ fn finishAir(cg: *CodeGen, inst: Air.Inst.Index, result: WValue, operands: []con |
| 435 | if (!dies) continue; | 466 | if (!dies) continue; |
| 436 | processDeath(cg, operand); | 467 | processDeath(cg, operand); |
| 437 | } | 468 | } |
| 438 | if (aliased_operand) |operand| final_result.local.references = operand.local.references; | 469 | for (branch_deaths) |death| cg.processDeath(death.toRef()); |
| 439 | try cg.finishAirResult(inst, final_result); | 470 | try cg.finishAirResult(inst, result); |
| 440 | } | 471 | } |
| 441 | | 472 | |
| 442 | fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerError!void { | 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,8 +477,7 @@ fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerErro |
| 446 | result | 477 | result |
| 447 | else | 478 | else |
| 448 | try result.toLocal(cg, cg.typeOfIndex(inst)); | 479 | try result.toLocal(cg, cg.typeOfIndex(inst)); |
| 449 | const branch = cg.currentBranch(); | 480 | try cg.values.putNoClobber(cg.gpa, inst.toRef(), trackable_result); |
| 450 | branch.values.putAssumeCapacityNoClobber(inst.toRef(), trackable_result); | | |
| 451 | } | 481 | } |
| 452 | | 482 | |
| 453 | if (std.debug.runtime_safety) { | 483 | if (std.debug.runtime_safety) { |
| ... | @@ -455,17 +485,16 @@ fn finishAirResult(cg: *CodeGen, inst: Air.Inst.Index, result: WValue) InnerErro | ... | @@ -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 { | 488 | fn pushBranch(cg: *CodeGen) InnerError!void { |
| 459 | values: ValueTable = .{}, | 489 | var snapshot = try cg.local_pool.clone(cg.gpa); |
| 460 | | 490 | errdefer snapshot.deinit(cg.gpa); |
| 461 | fn deinit(branch: *Branch, gpa: Allocator) void { | 491 | try cg.branches.append(cg.gpa, snapshot); |
| 462 | branch.values.deinit(gpa); | 492 | } |
| 463 | branch.* = undefined; | | |
| 464 | } | | |
| 465 | }; | | |
| 466 | | 493 | |
| 467 | inline fn currentBranch(cg: *CodeGen) *Branch { | 494 | fn popBranch(cg: *CodeGen) void { |
| 468 | return &cg.branches.items[cg.branches.items.len - 1]; | 495 | var snapshot = cg.branches.pop().?; |
| | 496 | defer snapshot.deinit(cg.gpa); |
| | 497 | snapshot.restore(cg); |
| 469 | } | 498 | } |
| 470 | | 499 | |
| 471 | fn feed(cg: *CodeGen, bt: *Air.Liveness.BigTomb, operand: Air.Inst.Ref) void { | 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,20 +505,9 @@ fn feed(cg: *CodeGen, bt: *Air.Liveness.BigTomb, operand: Air.Inst.Ref) void { |
| 476 | | 505 | |
| 477 | fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void { | 506 | fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void { |
| 478 | if (ref.toIndex() == null) return; | 507 | if (ref.toIndex() == null) return; |
| 479 | // Branches are currently only allowed to free locals allocated | 508 | const value = cg.values.getPtr(ref) orelse unreachable; |
| 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; | | |
| 483 | if (value.* != .local) return; | 509 | if (value.* != .local) return; |
| 484 | const reserved_indexes = cg.args.len + @intFromBool(cg.return_value != .none); | 510 | cg.releaseLocal(value.local); |
| 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 | } | | |
| 493 | } | 511 | } |
| 494 | | 512 | |
| 495 | pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void { | 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,7 +726,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { |
| 708 | switch (value) { | 726 | switch (value) { |
| 709 | .dead => unreachable, // reference to free'd `WValue` | 727 | .dead => unreachable, // reference to free'd `WValue` |
| 710 | .none, .stack => {}, // no-op | 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 | .imm32 => |val| try cg.addImm32(val), | 730 | .imm32 => |val| try cg.addImm32(val), |
| 713 | .imm64 => |val| try cg.addImm64(val), | 731 | .imm64 => |val| try cg.addImm64(val), |
| 714 | .imm128 => |val| try cg.addImm128(val), | 732 | .imm128 => |val| try cg.addImm128(val), |
| ... | @@ -780,22 +798,28 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { | ... | @@ -780,22 +798,28 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { |
| 780 | /// Emits the base address for a memory instruction. | 798 | /// Emits the base address for a memory instruction. |
| 781 | fn emitMemBase(cg: *CodeGen, ptr: WValue) InnerError!void { | 799 | fn emitMemBase(cg: *CodeGen, ptr: WValue) InnerError!void { |
| 782 | switch (ptr) { | 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 | else => try cg.emitWValue(ptr), | 802 | else => try cg.emitWValue(ptr), |
| 785 | } | 803 | } |
| 786 | } | 804 | } |
| 787 | | 805 | |
| 788 | /// From a reference, returns its resolved `WValue`. | 806 | fn appendFreeLocal(cg: *CodeGen, index: u32) void { |
| 789 | /// It's illegal to provide a `Air.Inst.Ref` that hasn't been resolved yet. | 807 | assert(index >= cg.wasm_param_count); |
| 790 | fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue { | 808 | const mir_index = index - cg.wasm_param_count; |
| 791 | var index = cg.branches.items.len; | 809 | const valtype = cg.mir_locals.items[mir_index]; |
| 792 | while (index > 0) : (index -= 1) { | 810 | const free_list = cg.local_pool.freeList(valtype); |
| 793 | const branch = cg.branches.items[index - 1]; | 811 | cg.local_pool.locals.items[mir_index] = .{ .next_free = free_list.* }; |
| 794 | if (branch.values.getPtr(ref)) |value| { | 812 | free_list.* = .init(index); |
| 795 | return value; | 813 | log.debug("freed local ({d}) of type {}", .{ index, valtype }); |
| 796 | } | 814 | } |
| 797 | } | 815 | |
| 798 | unreachable; // developer-error: This can only be called on resolved instructions. Use `resolveInst` instead. | 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 | /// Creates one locals for a given `Type`. | 825 | /// Creates one locals for a given `Type`. |
| ... | @@ -803,16 +827,13 @@ fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue { | ... | @@ -803,16 +827,13 @@ fn getResolvedInst(cg: *CodeGen, ref: Air.Inst.Ref) *WValue { |
| 803 | fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { | 827 | fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 804 | const zcu = cg.pt.zcu; | 828 | const zcu = cg.pt.zcu; |
| 805 | const valtype = typeToValtype(ty, zcu, cg.target); | 829 | const valtype = typeToValtype(ty, zcu, cg.target); |
| 806 | const index_or_null = switch (valtype) { | 830 | const free_list = cg.local_pool.freeList(valtype); |
| 807 | .i32 => cg.free_locals_i32.pop(), | 831 | if (free_list.unwrap()) |index| { |
| 808 | .i64 => cg.free_locals_i64.pop(), | 832 | const local = &cg.local_pool.locals.items[index - cg.wasm_param_count]; |
| 809 | .f32 => cg.free_locals_f32.pop(), | 833 | free_list.* = local.next_free; |
| 810 | .f64 => cg.free_locals_f64.pop(), | 834 | local.* = .{ .ref_count = 1 }; |
| 811 | .v128 => cg.free_locals_v128.pop(), | | |
| 812 | }; | | |
| 813 | if (index_or_null) |index| { | | |
| 814 | log.debug("reusing local ({d}) of type {}", .{ index, valtype }); | 835 | log.debug("reusing local ({d}) of type {}", .{ index, valtype }); |
| 815 | return .{ .local = .{ .value = index, .references = 1 } }; | 836 | return .{ .local = index }; |
| 816 | } | 837 | } |
| 817 | log.debug("new local of type {}", .{valtype}); | 838 | log.debug("new local of type {}", .{valtype}); |
| 818 | return cg.ensureAllocLocal(ty); | 839 | return cg.ensureAllocLocal(ty); |
| ... | @@ -822,10 +843,13 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { | ... | @@ -822,10 +843,13 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 822 | /// to use a zero-initialized local. | 843 | /// to use a zero-initialized local. |
| 823 | fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { | 844 | fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 824 | const zcu = cg.pt.zcu; | 845 | const zcu = cg.pt.zcu; |
| 825 | try cg.mir_locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target)); | 846 | try cg.mir_locals.ensureUnusedCapacity(cg.gpa, 1); |
| 826 | const initial_index = cg.local_index; | 847 | try cg.local_pool.locals.ensureUnusedCapacity(cg.gpa, 1); |
| 827 | cg.local_index += 1; | 848 | assert(cg.mir_locals.items.len == cg.local_pool.locals.items.len); |
| 828 | return .{ .local = .{ .value = initial_index, .references = 1 } }; | 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 | pub const Error = codegen.Error; | 855 | pub const Error = codegen.Error; |
| ... | @@ -867,7 +891,6 @@ pub fn generate( | ... | @@ -867,7 +891,6 @@ pub fn generate( |
| 867 | .args = cc_result.args, | 891 | .args = cc_result.args, |
| 868 | .return_value = cc_result.return_value, | 892 | .return_value = cc_result.return_value, |
| 869 | .varargs = cc_result.varargs, | 893 | .varargs = cc_result.varargs, |
| 870 | .local_index = cc_result.local_index, | | |
| 871 | .mir_instructions = .empty, | 894 | .mir_instructions = .empty, |
| 872 | .mir_extra = .empty, | 895 | .mir_extra = .empty, |
| 873 | .mir_locals = .empty, | 896 | .mir_locals = .empty, |
| ... | @@ -890,17 +913,10 @@ pub fn generate( | ... | @@ -890,17 +913,10 @@ pub fn generate( |
| 890 | | 913 | |
| 891 | fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { | 914 | fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { |
| 892 | const zcu = cg.pt.zcu; | 915 | const zcu = cg.pt.zcu; |
| 893 | // branch used for const values | 916 | cg.wasm_param_count = @intCast(cg.args.len + @intFromBool(cg.return_value != .none) + @intFromBool(cg.varargs != .none)); |
| 894 | try cg.branches.append(cg.gpa, .{}); | 917 | cg.initial_stack_value = try cg.ensureAllocLocal(Type.usize); |
| 895 | // func scope branch | 918 | cg.bottom_stack_value = try cg.ensureAllocLocal(Type.usize); |
| 896 | try cg.branches.append(cg.gpa, .{}); | 919 | defer assert(cg.branches.items.len == 0); |
| 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 | } | | |
| 904 | // Generate MIR for function body | 920 | // Generate MIR for function body |
| 905 | try cg.genBody(cg.air.getMainBody()); | 921 | try cg.genBody(cg.air.getMainBody()); |
| 906 | | 922 | |
| ... | @@ -925,11 +941,11 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { | ... | @@ -925,11 +941,11 @@ fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { |
| 925 | .instructions = cg.mir_instructions.toOwnedSlice(), | 941 | .instructions = cg.mir_instructions.toOwnedSlice(), |
| 926 | .extra = cg.mir_extra.toOwnedSliceAssert(), | 942 | .extra = cg.mir_extra.toOwnedSliceAssert(), |
| 927 | .locals = cg.mir_locals.toOwnedSliceAssert(), | 943 | .locals = cg.mir_locals.toOwnedSliceAssert(), |
| 928 | .prologue = if (cg.initial_stack_value == .none) .none else .{ | 944 | .prologue = .{ |
| 929 | .sp_local = cg.initial_stack_value.local.value, | 945 | .sp_local = cg.initial_stack_value.local, |
| 930 | .flags = .{ .stack_alignment = cg.stack_alignment }, | 946 | .flags = .{ .stack_alignment = cg.stack_alignment }, |
| 931 | .stack_size = cg.stack_size, | 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 | .uavs = cg.mir_uavs.move(), | 950 | .uavs = cg.mir_uavs.move(), |
| 935 | .indirect_function_set = cg.mir_indirect_function_set.move(), | 951 | .indirect_function_set = cg.mir_indirect_function_set.move(), |
| ... | @@ -976,7 +992,7 @@ fn resolveCallingConventionValues( | ... | @@ -976,7 +992,7 @@ fn resolveCallingConventionValues( |
| 976 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, target)) { | 992 | if (firstParamSRet(fn_info.cc, Type.fromInterned(fn_info.return_type), zcu, target)) { |
| 977 | // the sret arg will be passed as first argument, therefore we | 993 | // the sret arg will be passed as first argument, therefore we |
| 978 | // set the `return_value` before allocating locals for regular args. | 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 | result.local_index += 1; | 996 | result.local_index += 1; |
| 981 | } | 997 | } |
| 982 | | 998 | |
| ... | @@ -987,7 +1003,7 @@ fn resolveCallingConventionValues( | ... | @@ -987,7 +1003,7 @@ fn resolveCallingConventionValues( |
| 987 | continue; | 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 | result.local_index += 1; | 1007 | result.local_index += 1; |
| 992 | } | 1008 | } |
| 993 | }, | 1009 | }, |
| ... | @@ -1000,17 +1016,17 @@ fn resolveCallingConventionValues( | ... | @@ -1000,17 +1016,17 @@ fn resolveCallingConventionValues( |
| 1000 | | 1016 | |
| 1001 | switch (abi.classifyType(param_ty, zcu, target)) { | 1017 | switch (abi.classifyType(param_ty, zcu, target)) { |
| 1002 | .direct, .indirect => { | 1018 | .direct, .indirect => { |
| 1003 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | 1019 | try args.append(.{ .local = result.local_index }); |
| 1004 | result.local_index += 1; | 1020 | result.local_index += 1; |
| 1005 | }, | 1021 | }, |
| 1006 | .double_i64 => { | 1022 | .double_i64 => { |
| 1007 | try args.append(.{ .local = .{ .value = result.local_index, .references = 1 } }); | 1023 | try args.append(.{ .local = result.local_index }); |
| 1008 | try args.append(.{ .local = .{ .value = result.local_index + 1, .references = 1 } }); | 1024 | try args.append(.{ .local = result.local_index + 1 }); |
| 1009 | result.local_index += 2; | 1025 | result.local_index += 2; |
| 1010 | }, | 1026 | }, |
| 1011 | .unrolled => |vector| { | 1027 | .unrolled => |vector| { |
| 1012 | for (0..vector.len) |_| { | 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 | result.local_index += 1; | 1030 | result.local_index += 1; |
| 1015 | } | 1031 | } |
| 1016 | }, | 1032 | }, |
| ... | @@ -1021,7 +1037,7 @@ fn resolveCallingConventionValues( | ... | @@ -1021,7 +1037,7 @@ fn resolveCallingConventionValues( |
| 1021 | } | 1037 | } |
| 1022 | | 1038 | |
| 1023 | if (fn_info.is_var_args) { | 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 | result.local_index += 1; | 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,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 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it | 1107 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it |
| 1104 | /// to the global stack pointer variable | 1108 | /// to the global stack pointer variable |
| 1105 | fn restoreStackPointer(cg: *CodeGen) !void { | 1109 | fn restoreStackPointer(cg: *CodeGen) !void { |
| 1106 | // only restore the pointer if it was initialized | | |
| 1107 | if (cg.initial_stack_value == .none) return; | | |
| 1108 | // Get the original stack pointer's value | 1110 | // Get the original stack pointer's value |
| 1109 | try cg.emitWValue(cg.initial_stack_value); | 1111 | try cg.emitWValue(cg.initial_stack_value); |
| 1110 | | 1112 | |
| ... | @@ -1144,10 +1146,6 @@ fn allocInt(cg: *CodeGen, int_ty: IntType) !WValue { | ... | @@ -1144,10 +1146,6 @@ fn allocInt(cg: *CodeGen, int_ty: IntType) !WValue { |
| 1144 | fn allocStackBytes(cg: *CodeGen, size: u32, alignment: Alignment) !WValue { | 1146 | fn allocStackBytes(cg: *CodeGen, size: u32, alignment: Alignment) !WValue { |
| 1145 | assert(size > 0); | 1147 | assert(size > 0); |
| 1146 | | 1148 | |
| 1147 | if (cg.initial_stack_value == .none) { | | |
| 1148 | try cg.initializeStack(); | | |
| 1149 | } | | |
| 1150 | | | |
| 1151 | cg.stack_alignment = cg.stack_alignment.max(alignment); | 1149 | cg.stack_alignment = cg.stack_alignment.max(alignment); |
| 1152 | | 1150 | |
| 1153 | const offset: u32 = @intCast(alignment.forward(cg.stack_size)); | 1151 | const offset: u32 = @intCast(alignment.forward(cg.stack_size)); |
| ... | @@ -1166,10 +1164,6 @@ fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue { | ... | @@ -1166,10 +1164,6 @@ fn allocStackPtr(cg: *CodeGen, inst: Air.Inst.Index) !WValue { |
| 1166 | const ptr_ty = cg.typeOfIndex(inst); | 1164 | const ptr_ty = cg.typeOfIndex(inst); |
| 1167 | const pointee_ty = ptr_ty.childType(zcu); | 1165 | const pointee_ty = ptr_ty.childType(zcu); |
| 1168 | | 1166 | |
| 1169 | if (cg.initial_stack_value == .none) { | | |
| 1170 | try cg.initializeStack(); | | |
| 1171 | } | | |
| 1172 | | | |
| 1173 | const abi_alignment = ptr_ty.ptrAlignment(zcu); | 1167 | const abi_alignment = ptr_ty.ptrAlignment(zcu); |
| 1174 | const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse { | 1168 | const abi_size = std.math.cast(u32, pointee_ty.abiSize(zcu)) orelse { |
| 1175 | return cg.fail("Type {f} with ABI size of {d} exceeds stack frame size", .{ | 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,7 +1928,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 1934 | continue; | 1928 | continue; |
| 1935 | } | 1929 | } |
| 1936 | const old_bookkeeping_value = cg.air_bookkeeping; | 1930 | const old_bookkeeping_value = cg.air_bookkeeping; |
| 1937 | try cg.currentBranch().values.ensureUnusedCapacity(cg.gpa, 1); | | |
| 1938 | try cg.genInst(inst); | 1931 | try cg.genInst(inst); |
| 1939 | | 1932 | |
| 1940 | if (std.debug.runtime_safety and cg.air_bookkeeping < old_bookkeeping_value + 1) { | 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,11 +2153,11 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 2160 | .direct => |scalar_type| { | 2153 | .direct => |scalar_type| { |
| 2161 | if (!isByRef(ret_ty, zcu, cg.target)) { | 2154 | if (!isByRef(ret_ty, zcu, cg.target)) { |
| 2162 | const result_local = try cg.allocLocal(ret_ty); | 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 | break :result_value result_local; | 2157 | break :result_value result_local; |
| 2165 | } else { | 2158 | } else { |
| 2166 | const result_local = try cg.allocLocal(scalar_type); | 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 | const result = try cg.allocStack(ret_ty); | 2161 | const result = try cg.allocStack(ret_ty); |
| 2169 | try cg.store(result, result_local, scalar_type, .{}); | 2162 | try cg.store(result, result_local, scalar_type, .{}); |
| 2170 | break :result_value result; | 2163 | break :result_value result; |
| ... | @@ -2175,7 +2168,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) | ... | @@ -2175,7 +2168,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 2175 | assert(vector.len == 1); | 2168 | assert(vector.len == 1); |
| 2176 | const result_local = try cg.allocLocal(vector.elem_type); | 2169 | const result_local = try cg.allocLocal(vector.elem_type); |
| 2177 | // save call result from operand stack | 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 | const result = try cg.allocStack(ret_ty); | 2172 | const result = try cg.allocStack(ret_ty); |
| 2180 | try cg.store(result, result_local, vector.elem_type, .{}); | 2173 | try cg.store(result, result_local, vector.elem_type, .{}); |
| 2181 | break :result_value result; | 2174 | break :result_value result; |
| ... | @@ -2183,7 +2176,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) | ... | @@ -2183,7 +2176,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 2183 | } | 2176 | } |
| 2184 | } else { | 2177 | } else { |
| 2185 | const result_local = try cg.allocLocal(ret_ty); | 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 | break :result_value result_local; | 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,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 | try cg.emitWValue(operand); | 2248 | try cg.emitWValue(operand); |
| 2256 | try cg.emitWValue(arg_ptr); | 2249 | try cg.emitWValue(arg_ptr); |
| ... | @@ -2272,7 +2265,7 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2272,7 +2265,7 @@ fn airVaArg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2272 | try cg.addTag(.f32_demote_f64); | 2265 | try cg.addTag(.f32_demote_f64); |
| 2273 | | 2266 | |
| 2274 | const result_local = try cg.allocLocal(Type.f32); | 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 | break :result result_local; | 2269 | break :result result_local; |
| 2277 | } else try cg.load(arg_ptr, ty, .{}); | 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,7 +3293,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3300 | | 3293 | |
| 3301 | var mask = try cg.allocLocal(Type.i32); | 3294 | var mask = try cg.allocLocal(Type.i32); |
| 3302 | defer mask.free(cg); | 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 | try cg.emitWValue(operand); | 3298 | try cg.emitWValue(operand); |
| 3306 | try cg.addTag(.i32_xor); | 3299 | try cg.addTag(.i32_xor); |
| ... | @@ -3315,7 +3308,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { | ... | @@ -3315,7 +3308,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3315 | | 3308 | |
| 3316 | var mask = try cg.allocLocal(Type.i64); | 3309 | var mask = try cg.allocLocal(Type.i64); |
| 3317 | defer mask.free(cg); | 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 | try cg.emitWValue(operand); | 3313 | try cg.emitWValue(operand); |
| 3321 | try cg.addTag(.i64_xor); | 3314 | try cg.addTag(.i64_xor); |
| ... | @@ -3337,7 +3330,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { | ... | @@ -3337,7 +3330,7 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3337 | | 3330 | |
| 3338 | var tmp = try cg.allocLocal(Type.u64); | 3331 | var tmp = try cg.allocLocal(Type.u64); |
| 3339 | defer tmp.free(cg); | 3332 | defer tmp.free(cg); |
| 3340 | try cg.addLocal(.local_tee, tmp.local.value); | 3333 | try cg.addLocal(.local_tee, tmp.local); |
| 3341 | try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset }); | 3334 | try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset }); |
| 3342 | try cg.emitWValue(tmp); | 3335 | try cg.emitWValue(tmp); |
| 3343 | try cg.store(.stack, .stack, .u64, .{ .offset = mask_offset + 8 }); | 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,7 +4026,7 @@ fn toLocalInt(cg: *CodeGen, value: WValue, int_ty: IntType) InnerError!WValue { |
| 4033 | else => return cg.fail("TODO: Support toLocalInt for integer bitsize: {d}", .{int_ty.bits}), | 4026 | else => return cg.fail("TODO: Support toLocalInt for integer bitsize: {d}", .{int_ty.bits}), |
| 4034 | }; | 4027 | }; |
| 4035 | const new_local = try cg.allocLocal(ty); | 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 | return new_local; | 4030 | return new_local; |
| 4038 | }, | 4031 | }, |
| 4039 | .local, .stack_offset => return value, | 4032 | .local, .stack_offset => return value, |
| ... | @@ -4112,7 +4105,7 @@ fn intSubOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro | ... | @@ -4112,7 +4105,7 @@ fn intSubOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro |
| 4112 | fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult { | 4105 | fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult { |
| 4113 | const overflow_bit = try cg.allocLocal(Type.u32); | 4106 | const overflow_bit = try cg.allocLocal(Type.u32); |
| 4114 | try cg.addImm32(0); | 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 | const result_val = if (int_ty.bits <= 32) blk: { | 4110 | const result_val = if (int_ty.bits <= 32) blk: { |
| 4118 | const new_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = 64 }; | 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,7 +4119,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner |
| 4126 | | 4119 | |
| 4127 | const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp); | 4120 | const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp); |
| 4128 | _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op); | 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 | break :blk res_tmp; | 4123 | break :blk res_tmp; |
| 4131 | } else if (int_ty.bits <= 64) blk: { | 4124 | } else if (int_ty.bits <= 64) blk: { |
| 4132 | const new_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = 128 }; | 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,7 +4133,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner |
| 4140 | | 4133 | |
| 4141 | const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp); | 4134 | const res_upcast = try cg.intCast(new_ty, int_ty, res_tmp); |
| 4142 | _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op); | 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 | break :blk res_tmp; | 4137 | break :blk res_tmp; |
| 4145 | } else if (int_ty.bits == 128 and int_ty.is_signed) blk: { | 4138 | } else if (int_ty.bits == 128 and int_ty.is_signed) blk: { |
| 4146 | const overflow_ret = try cg.allocStack(Type.i32); | 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,7 +4144,7 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner |
| 4151 | &.{ lhs, rhs, overflow_ret }, | 4144 | &.{ lhs, rhs, overflow_ret }, |
| 4152 | ); | 4145 | ); |
| 4153 | _ = try cg.load(overflow_ret, .i32, .{}); | 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 | break :blk res; | 4148 | break :blk res; |
| 4156 | } else { | 4149 | } else { |
| 4157 | const result = try cg.allocInt(int_ty); | 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,17 +5225,17 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const |
| 5232 | .label = cg.block_depth, | 5225 | .label = cg.block_depth, |
| 5233 | .value = block_result, | 5226 | .value = block_result, |
| 5234 | }); | 5227 | }); |
| | 5228 | defer assert(cg.blocks.swapRemove(inst)); |
| 5235 | | 5229 | |
| 5236 | { | 5230 | { |
| 5237 | try cg.branches.append(cg.gpa, .{}); | 5231 | try cg.pushBranch(); |
| 5238 | defer { | 5232 | defer cg.popBranch(); |
| 5239 | var branch = cg.branches.pop().?; | | |
| 5240 | branch.deinit(cg.gpa); | | |
| 5241 | } | | |
| 5242 | try cg.genBody(body); | 5233 | try cg.genBody(body); |
| 5243 | try cg.endBlock(); | 5234 | try cg.endBlock(); |
| 5244 | } | 5235 | } |
| 5245 | | 5236 | |
| | 5237 | for (cg.liveness.getBlock(inst).deaths) |death| cg.processDeath(death.toRef()); |
| | 5238 | |
| 5246 | return cg.finishAir(inst, block_result, &.{}); | 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,6 +5275,7 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5282 | const condition = try cg.resolveInst(cond_br.condition); | 5275 | const condition = try cg.resolveInst(cond_br.condition); |
| 5283 | const then_body = cond_br.then_body; | 5276 | const then_body = cond_br.then_body; |
| 5284 | const else_body = cond_br.else_body; | 5277 | const else_body = cond_br.else_body; |
| | 5278 | const branch_liveness = cg.liveness.getCondBr(inst); |
| 5285 | | 5279 | |
| 5286 | // result type is always noreturn, so use `block_empty` as type. | 5280 | // result type is always noreturn, so use `block_empty` as type. |
| 5287 | try cg.startBlock(.block, .empty); | 5281 | try cg.startBlock(.block, .empty); |
| ... | @@ -5293,24 +5287,21 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5293,24 +5287,21 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5293 | // and continue with the then codepath | 5287 | // and continue with the then codepath |
| 5294 | try cg.addLabel(.br_if, 0); | 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(.{}); | 5293 | try cg.pushBranch(); |
| 5299 | defer { | 5294 | defer cg.popBranch(); |
| 5300 | var else_stack = cg.branches.pop().?; | 5295 | for (branch_liveness.else_deaths) |death| cg.processDeath(death.toRef()); |
| 5301 | else_stack.deinit(cg.gpa); | | |
| 5302 | } | | |
| 5303 | try cg.genBody(else_body); | 5296 | try cg.genBody(else_body); |
| 5304 | try cg.endBlock(); | 5297 | try cg.endBlock(); |
| 5305 | } | 5298 | } |
| 5306 | | 5299 | |
| 5307 | // Outer block that matches the condition | 5300 | // Outer block that matches the condition |
| 5308 | { | 5301 | { |
| 5309 | cg.branches.appendAssumeCapacity(.{}); | 5302 | try cg.pushBranch(); |
| 5310 | defer { | 5303 | defer cg.popBranch(); |
| 5311 | var then_stack = cg.branches.pop().?; | 5304 | for (branch_liveness.then_deaths) |death| cg.processDeath(death.toRef()); |
| 5312 | then_stack.deinit(cg.gpa); | | |
| 5313 | } | | |
| 5314 | try cg.genBody(then_body); | 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,18 +5337,18 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne |
| 5346 | try cg.startBlock(.block, .empty); | 5337 | try cg.startBlock(.block, .empty); |
| 5347 | | 5338 | |
| 5348 | try cg.addImm32(if (op == .eq) 0 else 1); | 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 | _ = try cg.isNull(lhs, operand_ty, .i32_eq, .value); | 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 | _ = try cg.isNull(rhs, operand_ty, .i32_eq, .value); | 5344 | _ = try cg.isNull(rhs, operand_ty, .i32_eq, .value); |
| 5354 | try cg.addTag(.i32_ne); | 5345 | try cg.addTag(.i32_ne); |
| 5355 | try cg.addLabel(.br_if, 0); | 5346 | try cg.addLabel(.br_if, 0); |
| 5356 | | 5347 | |
| 5357 | try cg.addImm32(if (op == .eq) 1 else 0); | 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 | try cg.addLabel(.br_if, 0); | 5352 | try cg.addLabel(.br_if, 0); |
| 5362 | | 5353 | |
| 5363 | _ = try cg.load(lhs, payload_ty, .{}); | 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,10 +5360,10 @@ fn airCmp(cg: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) Inne |
| 5369 | _ = try cg.intCmp(.fromType(cg, payload_ty), op, .stack, .stack); | 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 | try cg.endBlock(); | 5364 | try cg.endBlock(); |
| 5374 | | 5365 | |
| 5375 | try cg.addLocal(.local_get, result.local.value); | 5366 | try cg.addLocal(.local_get, result.local); |
| 5376 | try cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); | 5367 | try cg.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs }); |
| 5377 | } else { | 5368 | } else { |
| 5378 | const result = try cg.intCmp(.fromType(cg, operand_ty), op, lhs, rhs); | 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,7 +5561,7 @@ fn airBr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5570 | if (block.value != .none) { | 5561 | if (block.value != .none) { |
| 5571 | const operand = try cg.resolveInst(br.operand); | 5562 | const operand = try cg.resolveInst(br.operand); |
| 5572 | try cg.emitWValue(operand); | 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 | // We map every block to its block index. | 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,24 +5845,29 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner |
| 5854 | | 5845 | |
| 5855 | const switch_br = cg.air.unwrapSwitch(inst); | 5846 | const switch_br = cg.air.unwrapSwitch(inst); |
| 5856 | const target_ty = cg.typeOf(switch_br.operand); | 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 | assert(target_ty.hasRuntimeBits(zcu)); | 5851 | assert(target_ty.hasRuntimeBits(zcu)); |
| 5859 | | 5852 | |
| 5860 | // swap target value with placeholder local, for dispatching | 5853 | // swap target value with placeholder local, for dispatching |
| | 5854 | var owned_target: WValue = .none; |
| | 5855 | defer owned_target.free(cg); |
| 5861 | const target = if (is_dispatch_loop) target: { | 5856 | const target = if (is_dispatch_loop) target: { |
| 5862 | const initial_target = try cg.resolveInst(switch_br.operand); | 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 | try cg.emitWValue(initial_target); | 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 | try cg.startBlock(.loop, .empty); // dispatch loop start | 5862 | try cg.startBlock(.loop, .empty); // dispatch loop start |
| 5868 | try cg.blocks.putNoClobber(cg.gpa, inst, .{ | 5863 | try cg.blocks.putNoClobber(cg.gpa, inst, .{ |
| 5869 | .label = cg.block_depth, | 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 | } else try cg.resolveInst(switch_br.operand); | 5869 | } else try cg.resolveInst(switch_br.operand); |
| | 5870 | defer if (is_dispatch_loop) assert(cg.blocks.swapRemove(inst)); |
| 5875 | | 5871 | |
| 5876 | const has_else_body = switch_br.else_body_len != 0; | 5872 | const has_else_body = switch_br.else_body_len != 0; |
| 5877 | const branch_count = switch_br.cases_len + 1; // if else branch is missing, we trap when failing all conditions | 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,12 +5879,14 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner |
| 5883 | var it = switch_br.iterateCases(); | 5879 | var it = switch_br.iterateCases(); |
| 5884 | const else_body = it.elseBody(); | 5880 | const else_body = it.elseBody(); |
| 5885 | | 5881 | |
| 5886 | cg.branches.appendAssumeCapacity(.{}); | 5882 | if (cg.liveness.operandDies(inst, 0)) cg.processDeath(switch_br.operand); |
| 5887 | defer { | 5883 | |
| 5888 | var else_branch = cg.branches.pop().?; | 5884 | { |
| 5889 | else_branch.deinit(cg.gpa); | 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 | if (is_dispatch_loop) { | 5891 | if (is_dispatch_loop) { |
| 5894 | try cg.endBlock(); // dispatch loop end | 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,16 +6010,18 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner |
| 6012 | try cg.addLabel(.br, branch_count - 1); | 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 | var cases_it = switch_br.iterateCases(); | 6015 | var cases_it = switch_br.iterateCases(); |
| 6016 | while (cases_it.next()) |case| { | 6016 | while (cases_it.next()) |case| { |
| 6017 | try cg.endBlock(); | 6017 | try cg.endBlock(); |
| 6018 | | 6018 | |
| 6019 | cg.branches.appendAssumeCapacity(.{}); | 6019 | { |
| 6020 | defer { | 6020 | try cg.pushBranch(); |
| 6021 | var case_branch = cg.branches.pop().?; | 6021 | defer cg.popBranch(); |
| 6022 | case_branch.deinit(cg.gpa); | 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 | try cg.addLabel(.br, branch_count - case.idx - 1); // matching case found and executed => exit switch | 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,12 +6030,12 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index, is_dispatch_loop: bool) Inner |
| 6030 | if (has_else_body) { | 6030 | if (has_else_body) { |
| 6031 | const else_body = cases_it.elseBody(); | 6031 | const else_body = cases_it.elseBody(); |
| 6032 | | 6032 | |
| 6033 | cg.branches.appendAssumeCapacity(.{}); | 6033 | { |
| 6034 | defer { | 6034 | try cg.pushBranch(); |
| 6035 | var else_branch = cg.branches.pop().?; | 6035 | defer cg.popBranch(); |
| 6036 | else_branch.deinit(cg.gpa); | 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 | } else { | 6039 | } else { |
| 6040 | try cg.addTag(.@"unreachable"); | 6040 | try cg.addTag(.@"unreachable"); |
| 6041 | } | 6041 | } |
| ... | @@ -6055,7 +6055,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -6055,7 +6055,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6055 | | 6055 | |
| 6056 | const operand = try cg.resolveInst(br.operand); | 6056 | const operand = try cg.resolveInst(br.operand); |
| 6057 | try cg.emitWValue(operand); | 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 | const idx: u32 = cg.block_depth - switch_loop.label; | 6060 | const idx: u32 = cg.block_depth - switch_loop.label; |
| 6061 | try cg.addLabel(.br, idx); | 6061 | try cg.addLabel(.br, idx); |
| ... | @@ -6630,7 +6630,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) | ... | @@ -6630,7 +6630,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) |
| 6630 | try cg.addTag(.i64_mul); | 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 | break :blk new_len; | 6634 | break :blk new_len; |
| 6635 | } else len, | 6635 | } else len, |
| 6636 | }; | 6636 | }; |
| ... | @@ -6640,7 +6640,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) | ... | @@ -6640,7 +6640,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) |
| 6640 | var new_ptr = try cg.allocLocal(Type.usize); | 6640 | var new_ptr = try cg.allocLocal(Type.usize); |
| 6641 | defer new_ptr.free(cg); | 6641 | defer new_ptr.free(cg); |
| 6642 | try cg.emitWValue(ptr); | 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 | // get the loop conditional: if current pointer address equals final pointer's address | 6645 | // get the loop conditional: if current pointer address equals final pointer's address |
| 6646 | try cg.emitWValue(ptr); | 6646 | try cg.emitWValue(ptr); |
| ... | @@ -6649,7 +6649,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) | ... | @@ -6649,7 +6649,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) |
| 6649 | .wasm32 => try cg.addTag(.i32_add), | 6649 | .wasm32 => try cg.addTag(.i32_add), |
| 6650 | .wasm64 => try cg.addTag(.i64_add), | 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 | // outer block to jump to when loop is done | 6654 | // outer block to jump to when loop is done |
| 6655 | try cg.startBlock(.block, .empty); | 6655 | try cg.startBlock(.block, .empty); |
| ... | @@ -6679,7 +6679,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) | ... | @@ -6679,7 +6679,7 @@ fn memset(cg: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) |
| 6679 | try cg.addTag(.i64_add); | 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 | // end of loop | 6684 | // end of loop |
| 6685 | try cg.addLabel(.br, 0); // jump to start of loop | 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,7 +7186,7 @@ fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7186 | try cg.emitWValue(slice_len); | 7186 | try cg.emitWValue(slice_len); |
| 7187 | try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) }); | 7187 | try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) }); |
| 7188 | try cg.addTag(.i32_mul); | 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 | break :blk slice_len; | 7191 | break :blk slice_len; |
| 7192 | }, | 7192 | }, |
| ... | @@ -7217,7 +7217,7 @@ fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7217,7 +7217,7 @@ fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7217 | try cg.emitWValue(slice_len); | 7217 | try cg.emitWValue(slice_len); |
| 7218 | try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) }); | 7218 | try cg.emitWValue(.{ .imm32 = @as(u32, @intCast(ptr_elem_ty.abiSize(zcu))) }); |
| 7219 | try cg.addTag(.i32_mul); | 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 | break :blk slice_len; | 7222 | break :blk slice_len; |
| 7223 | }, | 7223 | }, |
| ... | @@ -7303,8 +7303,13 @@ fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7303,8 +7303,13 @@ fn airTry(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7303 | const body = unwrapped_try.else_body; | 7303 | const body = unwrapped_try.else_body; |
| 7304 | const err_union = try cg.resolveInst(unwrapped_try.error_union); | 7304 | const err_union = try cg.resolveInst(unwrapped_try.error_union); |
| 7305 | const err_union_ty = cg.typeOf(unwrapped_try.error_union); | 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); | 7306 | const result = try lowerTry(cg, inst, unwrapped_try.error_union, err_union, body, err_union_ty, false); |
| 7307 | return cg.finishAir(inst, result, &.{unwrapped_try.error_union}); | 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 | fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 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,20 +7318,26 @@ fn airTryPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7313 | const err_union_ptr = try cg.resolveInst(unwrapped_try.error_union_ptr); | 7318 | const err_union_ptr = try cg.resolveInst(unwrapped_try.error_union_ptr); |
| 7314 | const body = unwrapped_try.else_body; | 7319 | const body = unwrapped_try.else_body; |
| 7315 | const err_union_ty = cg.typeOf(unwrapped_try.error_union_ptr).childType(zcu); | 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); | 7321 | const result = try lowerTry(cg, inst, unwrapped_try.error_union_ptr, err_union_ptr, body, err_union_ty, true); |
| 7317 | return cg.finishAir(inst, result, &.{unwrapped_try.error_union_ptr}); | 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 | fn lowerTry( | 7330 | fn lowerTry( |
| 7321 | cg: *CodeGen, | 7331 | cg: *CodeGen, |
| 7322 | inst: Air.Inst.Index, | 7332 | inst: Air.Inst.Index, |
| | 7333 | operand_ref: Air.Inst.Ref, |
| 7323 | err_union: WValue, | 7334 | err_union: WValue, |
| 7324 | body: []const Air.Inst.Index, | 7335 | body: []const Air.Inst.Index, |
| 7325 | err_union_ty: Type, | 7336 | err_union_ty: Type, |
| 7326 | operand_is_ptr: bool, | 7337 | operand_is_ptr: bool, |
| 7327 | ) InnerError!WValue { | 7338 | ) InnerError!WValue { |
| 7328 | _ = inst; | | |
| 7329 | const zcu = cg.pt.zcu; | 7339 | const zcu = cg.pt.zcu; |
| | 7340 | const branch_liveness = cg.liveness.getCondBr(inst); |
| 7330 | | 7341 | |
| 7331 | const pl_ty = err_union_ty.errorUnionPayload(zcu); | 7342 | const pl_ty = err_union_ty.errorUnionPayload(zcu); |
| 7332 | const pl_has_bits = pl_ty.hasRuntimeBits(zcu); | 7343 | const pl_has_bits = pl_ty.hasRuntimeBits(zcu); |
| ... | @@ -7349,12 +7360,13 @@ fn lowerTry( | ... | @@ -7349,12 +7360,13 @@ fn lowerTry( |
| 7349 | try cg.addTag(.i32_eqz); | 7360 | try cg.addTag(.i32_eqz); |
| 7350 | try cg.addLabel(.br_if, 0); // jump out of block when error is '0' | 7361 | try cg.addLabel(.br_if, 0); // jump out of block when error is '0' |
| 7351 | | 7362 | |
| 7352 | try cg.branches.append(cg.gpa, .{}); | 7363 | { |
| 7353 | defer { | 7364 | try cg.pushBranch(); |
| 7354 | var branch = cg.branches.pop().?; | 7365 | defer cg.popBranch(); |
| 7355 | branch.deinit(cg.gpa); | 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 | try cg.endBlock(); | 7370 | try cg.endBlock(); |
| 7359 | } | 7371 | } |
| 7360 | | 7372 | |
| ... | @@ -7367,8 +7379,7 @@ fn lowerTry( | ... | @@ -7367,8 +7379,7 @@ fn lowerTry( |
| 7367 | if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) { | 7379 | if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) { |
| 7368 | return cg.offsetPointer(err_union, pl_offset); | 7380 | return cg.offsetPointer(err_union, pl_offset); |
| 7369 | } | 7381 | } |
| 7370 | const payload = try cg.load(err_union, pl_ty, .{ .offset = pl_offset }); | 7382 | return cg.load(err_union, pl_ty, .{ .offset = pl_offset }); |
| 7371 | return payload.toLocal(cg, pl_ty); | | |
| 7372 | } | 7383 | } |
| 7373 | | 7384 | |
| 7374 | /// Calls a compiler-rt intrinsic by creating an undefined symbol, | 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,13 +7523,13 @@ fn airErrorSetHasValue(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7512 | // 'false' branch (i.e. error set does not have value | 7523 | // 'false' branch (i.e. error set does not have value |
| 7513 | // ensure we set local to 0 in case the local was re-used. | 7524 | // ensure we set local to 0 in case the local was re-used. |
| 7514 | try cg.addImm32(0); | 7525 | try cg.addImm32(0); |
| 7515 | try cg.addLocal(.local_set, result.local.value); | 7526 | try cg.addLocal(.local_set, result.local); |
| 7516 | try cg.addLabel(.br, 1); | 7527 | try cg.addLabel(.br, 1); |
| 7517 | try cg.endBlock(); | 7528 | try cg.endBlock(); |
| 7518 | | 7529 | |
| 7519 | // 'true' branch | 7530 | // 'true' branch |
| 7520 | try cg.addImm32(1); | 7531 | try cg.addImm32(1); |
| 7521 | try cg.addLocal(.local_set, result.local.value); | 7532 | try cg.addLocal(.local_set, result.local); |
| 7522 | try cg.addLabel(.br, 0); | 7533 | try cg.addLabel(.br, 0); |
| 7523 | try cg.endBlock(); | 7534 | try cg.endBlock(); |
| 7524 | | 7535 | |
| ... | @@ -7561,9 +7572,9 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7561,9 +7572,9 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7561 | .offset = ptr_operand.memOffset(), | 7572 | .offset = ptr_operand.memOffset(), |
| 7562 | .alignment = ty.abiAlignment(zcu).toStdMem(), | 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 | _ = try cg.intCmp(int_ty, .eq, .stack, expected_val); | 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 | break :val val_local; | 7578 | break :val val_local; |
| 7568 | } else val: { | 7579 | } else val: { |
| 7569 | if (ty.abiSize(zcu) > 8) { | 7580 | if (ty.abiSize(zcu) > 8) { |
| ... | @@ -7575,7 +7586,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7575,7 +7586,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7575 | try cg.emitWValue(new_val); | 7586 | try cg.emitWValue(new_val); |
| 7576 | try cg.emitWValue(ptr_val); | 7587 | try cg.emitWValue(ptr_val); |
| 7577 | _ = try cg.intCmp(int_ty, .eq, ptr_val, expected_val); | 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 | try cg.addTag(.select); | 7590 | try cg.addTag(.select); |
| 7580 | try cg.store(.stack, .stack, ty, .{ .offset = ptr_operand.memOffset() }); | 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,11 +7696,11 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7685 | }, | 7696 | }, |
| 7686 | ); | 7697 | ); |
| 7687 | const select_res = try cg.allocLocal(ty); | 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 | _ = try cg.intCmp(int_ty, .neq, .stack, value); // leave on stack so we can use it for br_if | 7700 | _ = try cg.intCmp(int_ty, .neq, .stack, value); // leave on stack so we can use it for br_if |
| 7690 | | 7701 | |
| 7691 | try cg.emitWValue(select_res); | 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 | try cg.addLabel(.br_if, 0); | 7705 | try cg.addLabel(.br_if, 0); |
| 7695 | try cg.endBlock(); | 7706 | try cg.endBlock(); |
| ... | @@ -7864,9 +7875,6 @@ fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7864,9 +7875,6 @@ fn airAtomicStore(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7864 | } | 7875 | } |
| 7865 | | 7876 | |
| 7866 | fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 7877 | fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7867 | if (cg.initial_stack_value == .none) { | | |
| 7868 | try cg.initializeStack(); | | |
| 7869 | } | | |
| 7870 | try cg.emitWValue(cg.bottom_stack_value); | 7878 | try cg.emitWValue(cg.bottom_stack_value); |
| 7871 | return cg.finishAir(inst, .stack, &.{}); | 7879 | return cg.finishAir(inst, .stack, &.{}); |
| 7872 | } | 7880 | } |
| ... | @@ -7912,7 +7920,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7912,7 +7920,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7912 | return cg.fail("Self-hosted wasm backend requires output constraint to be equal \"=r\"", .{}); | 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 | assert(!gop.found_existing); // first value | 7924 | assert(!gop.found_existing); // first value |
| 7917 | | 7925 | |
| 7918 | assert(it.next() == null); | 7926 | assert(it.next() == null); |
| ... | @@ -7933,7 +7941,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -7933,7 +7941,7 @@ fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7933 | try cg.emitWValue(operand); | 7941 | try cg.emitWValue(operand); |
| 7934 | const op_local = try WValue.toLocal(.stack, cg, cg.typeOf(input.operand)); | 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 | if (gop.found_existing) { | 7945 | if (gop.found_existing) { |
| 7938 | return cg.fail("Duplicate asm variable name \"{s}\"", .{name}); | 7946 | return cg.fail("Duplicate asm variable name \"{s}\"", .{name}); |
| 7939 | } | 7947 | } |