| ... | @@ -1605,10 +1605,16 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { | ... | @@ -1605,10 +1605,16 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { |
| 1605 | else => {}, | 1605 | else => {}, |
| 1606 | } | 1606 | } |
| 1607 | | 1607 | |
| 1608 | // TODO: We should probably lower this to a call to compiler_rt | 1608 | // allocate a local for the offset, and set it to 0. |
| 1609 | // But for now, we implement it manually | 1609 | // This to ensure that inside loops we correctly re-set the counter. |
| 1610 | var offset = try func.ensureAllocLocal(Type.usize); // local for counter | 1610 | var offset = try func.allocLocal(Type.usize); // local for counter |
| 1611 | defer offset.free(func); | 1611 | defer offset.free(func); |
| | 1612 | switch (func.arch()) { |
| | 1613 | .wasm32 => try func.addImm32(0), |
| | 1614 | .wasm64 => try func.addImm64(0), |
| | 1615 | else => unreachable, |
| | 1616 | } |
| | 1617 | try func.addLabel(.local_set, offset.local.value); |
| 1612 | | 1618 | |
| 1613 | // outer block to jump to when loop is done | 1619 | // outer block to jump to when loop is done |
| 1614 | try func.startBlock(.block, wasm.block_empty); | 1620 | try func.startBlock(.block, wasm.block_empty); |
| ... | @@ -3301,19 +3307,23 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3301,19 +3307,23 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3301 | { | 3307 | { |
| 3302 | func.branches.appendAssumeCapacity(.{}); | 3308 | func.branches.appendAssumeCapacity(.{}); |
| 3303 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len)); | 3309 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len)); |
| | 3310 | defer { |
| | 3311 | var else_stack = func.branches.pop(); |
| | 3312 | else_stack.deinit(func.gpa); |
| | 3313 | } |
| 3304 | try func.genBody(else_body); | 3314 | try func.genBody(else_body); |
| 3305 | try func.endBlock(); | 3315 | try func.endBlock(); |
| 3306 | var else_stack = func.branches.pop(); | | |
| 3307 | else_stack.deinit(func.gpa); | | |
| 3308 | } | 3316 | } |
| 3309 | | 3317 | |
| 3310 | // Outer block that matches the condition | 3318 | // Outer block that matches the condition |
| 3311 | { | 3319 | { |
| 3312 | func.branches.appendAssumeCapacity(.{}); | 3320 | func.branches.appendAssumeCapacity(.{}); |
| 3313 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len)); | 3321 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len)); |
| | 3322 | defer { |
| | 3323 | var then_stack = func.branches.pop(); |
| | 3324 | then_stack.deinit(func.gpa); |
| | 3325 | } |
| 3314 | try func.genBody(then_body); | 3326 | try func.genBody(then_body); |
| 3315 | var then_stack = func.branches.pop(); | | |
| 3316 | then_stack.deinit(func.gpa); | | |
| 3317 | } | 3327 | } |
| 3318 | | 3328 | |
| 3319 | func.finishAir(inst, .none, &.{}); | 3329 | func.finishAir(inst, .none, &.{}); |
| ... | @@ -3829,20 +3839,24 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3829,20 +3839,24 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3829 | } | 3839 | } |
| 3830 | func.branches.appendAssumeCapacity(.{}); | 3840 | func.branches.appendAssumeCapacity(.{}); |
| 3831 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len); | 3841 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len); |
| | 3842 | defer { |
| | 3843 | var case_branch = func.branches.pop(); |
| | 3844 | case_branch.deinit(func.gpa); |
| | 3845 | } |
| 3832 | try func.genBody(case.body); | 3846 | try func.genBody(case.body); |
| 3833 | try func.endBlock(); | 3847 | try func.endBlock(); |
| 3834 | var case_branch = func.branches.pop(); | | |
| 3835 | case_branch.deinit(func.gpa); | | |
| 3836 | } | 3848 | } |
| 3837 | | 3849 | |
| 3838 | if (has_else_body) { | 3850 | if (has_else_body) { |
| 3839 | func.branches.appendAssumeCapacity(.{}); | 3851 | func.branches.appendAssumeCapacity(.{}); |
| 3840 | const else_deaths = liveness.deaths.len - 1; | 3852 | const else_deaths = liveness.deaths.len - 1; |
| 3841 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[else_deaths].len); | 3853 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[else_deaths].len); |
| | 3854 | defer { |
| | 3855 | var else_branch = func.branches.pop(); |
| | 3856 | else_branch.deinit(func.gpa); |
| | 3857 | } |
| 3842 | try func.genBody(else_body); | 3858 | try func.genBody(else_body); |
| 3843 | try func.endBlock(); | 3859 | try func.endBlock(); |
| 3844 | var else_branch = func.branches.pop(); | | |
| 3845 | else_branch.deinit(func.gpa); | | |
| 3846 | } | 3860 | } |
| 3847 | func.finishAir(inst, .none, &.{}); | 3861 | func.finishAir(inst, .none, &.{}); |
| 3848 | } | 3862 | } |
| ... | @@ -3971,7 +3985,7 @@ fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -3971,7 +3985,7 @@ fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3971 | // write 'undefined' to the payload | 3985 | // write 'undefined' to the payload |
| 3972 | const payload_ptr = try func.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, func.target)), .new); | 3986 | const payload_ptr = try func.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, func.target)), .new); |
| 3973 | const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(func.target)); | 3987 | const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(func.target)); |
| 3974 | try func.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa }); | 3988 | try func.memset(Type.u8, payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaa }); |
| 3975 | | 3989 | |
| 3976 | break :result err_union; | 3990 | break :result err_union; |
| 3977 | }; | 3991 | }; |
| ... | @@ -4466,8 +4480,13 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void | ... | @@ -4466,8 +4480,13 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void |
| 4466 | .C, .Many => unreachable, | 4480 | .C, .Many => unreachable, |
| 4467 | }; | 4481 | }; |
| 4468 | | 4482 | |
| | 4483 | const elem_ty = if (ptr_ty.ptrSize() == .One) |
| | 4484 | ptr_ty.childType().childType() |
| | 4485 | else |
| | 4486 | ptr_ty.childType(); |
| | 4487 | |
| 4469 | const dst_ptr = try func.sliceOrArrayPtr(ptr, ptr_ty); | 4488 | const dst_ptr = try func.sliceOrArrayPtr(ptr, ptr_ty); |
| 4470 | try func.memset(dst_ptr, len, value); | 4489 | try func.memset(elem_ty, dst_ptr, len, value); |
| 4471 | | 4490 | |
| 4472 | func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); | 4491 | func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs }); |
| 4473 | } | 4492 | } |
| ... | @@ -4476,10 +4495,12 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void | ... | @@ -4476,10 +4495,12 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void |
| 4476 | /// When the user has enabled the bulk_memory feature, we lower | 4495 | /// When the user has enabled the bulk_memory feature, we lower |
| 4477 | /// this to wasm's memset instruction. When the feature is not present, | 4496 | /// this to wasm's memset instruction. When the feature is not present, |
| 4478 | /// we implement it manually. | 4497 | /// we implement it manually. |
| 4479 | fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!void { | 4498 | fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void { |
| | 4499 | const abi_size = @intCast(u32, elem_ty.abiSize(func.target)); |
| | 4500 | |
| 4480 | // When bulk_memory is enabled, we lower it to wasm's memset instruction. | 4501 | // When bulk_memory is enabled, we lower it to wasm's memset instruction. |
| 4481 | // If not, we lower it ourselves | 4502 | // If not, we lower it ourselves. |
| 4482 | if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory)) { | 4503 | if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory) and abi_size == 1) { |
| 4483 | try func.lowerToStack(ptr); | 4504 | try func.lowerToStack(ptr); |
| 4484 | try func.emitWValue(value); | 4505 | try func.emitWValue(value); |
| 4485 | try func.emitWValue(len); | 4506 | try func.emitWValue(len); |
| ... | @@ -4487,74 +4508,79 @@ fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!vo | ... | @@ -4487,74 +4508,79 @@ fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!vo |
| 4487 | return; | 4508 | return; |
| 4488 | } | 4509 | } |
| 4489 | | 4510 | |
| 4490 | // When the length is comptime-known we do the loop at codegen, rather | 4511 | const final_len = switch (len) { |
| 4491 | // than emitting a runtime loop into the binary | 4512 | .imm32 => |val| WValue{ .imm32 = val * abi_size }, |
| 4492 | switch (len) { | 4513 | .imm64 => |val| WValue{ .imm64 = val * abi_size }, |
| 4493 | .imm32, .imm64 => { | 4514 | else => if (abi_size != 1) blk: { |
| 4494 | const length = switch (len) { | 4515 | const new_len = try func.ensureAllocLocal(Type.usize); |
| 4495 | .imm32 => |val| val, | | |
| 4496 | .imm64 => |val| val, | | |
| 4497 | else => unreachable, | | |
| 4498 | }; | | |
| 4499 | | | |
| 4500 | var offset: u32 = 0; | | |
| 4501 | const base = ptr.offset(); | | |
| 4502 | while (offset < length) : (offset += 1) { | | |
| 4503 | try func.emitWValue(ptr); | | |
| 4504 | try func.emitWValue(value); | | |
| 4505 | switch (func.arch()) { | | |
| 4506 | .wasm32 => { | | |
| 4507 | try func.addMemArg(.i32_store8, .{ .offset = base + offset, .alignment = 1 }); | | |
| 4508 | }, | | |
| 4509 | .wasm64 => { | | |
| 4510 | try func.addMemArg(.i64_store8, .{ .offset = base + offset, .alignment = 1 }); | | |
| 4511 | }, | | |
| 4512 | else => unreachable, | | |
| 4513 | } | | |
| 4514 | } | | |
| 4515 | }, | | |
| 4516 | else => { | | |
| 4517 | // TODO: We should probably lower this to a call to compiler_rt | | |
| 4518 | // But for now, we implement it manually | | |
| 4519 | const offset = try func.ensureAllocLocal(Type.usize); // local for counter | | |
| 4520 | // outer block to jump to when loop is done | | |
| 4521 | try func.startBlock(.block, wasm.block_empty); | | |
| 4522 | try func.startBlock(.loop, wasm.block_empty); | | |
| 4523 | try func.emitWValue(offset); | | |
| 4524 | try func.emitWValue(len); | 4516 | try func.emitWValue(len); |
| 4525 | switch (func.arch()) { | 4517 | switch (func.arch()) { |
| 4526 | .wasm32 => try func.addTag(.i32_eq), | 4518 | .wasm32 => { |
| 4527 | .wasm64 => try func.addTag(.i64_eq), | 4519 | try func.emitWValue(.{ .imm32 = abi_size }); |
| 4528 | else => unreachable, | 4520 | try func.addTag(.i32_mul); |
| 4529 | } | 4521 | }, |
| 4530 | try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished) | 4522 | .wasm64 => { |
| 4531 | try func.emitWValue(ptr); | 4523 | try func.emitWValue(.{ .imm64 = abi_size }); |
| 4532 | try func.emitWValue(offset); | 4524 | try func.addTag(.i64_mul); |
| 4533 | switch (func.arch()) { | 4525 | }, |
| 4534 | .wasm32 => try func.addTag(.i32_add), | | |
| 4535 | .wasm64 => try func.addTag(.i64_add), | | |
| 4536 | else => unreachable, | | |
| 4537 | } | | |
| 4538 | try func.emitWValue(value); | | |
| 4539 | const mem_store_op: Mir.Inst.Tag = switch (func.arch()) { | | |
| 4540 | .wasm32 => .i32_store8, | | |
| 4541 | .wasm64 => .i64_store8, | | |
| 4542 | else => unreachable, | | |
| 4543 | }; | | |
| 4544 | try func.addMemArg(mem_store_op, .{ .offset = ptr.offset(), .alignment = 1 }); | | |
| 4545 | try func.emitWValue(offset); | | |
| 4546 | try func.addImm32(1); | | |
| 4547 | switch (func.arch()) { | | |
| 4548 | .wasm32 => try func.addTag(.i32_add), | | |
| 4549 | .wasm64 => try func.addTag(.i64_add), | | |
| 4550 | else => unreachable, | 4526 | else => unreachable, |
| 4551 | } | 4527 | } |
| 4552 | try func.addLabel(.local_set, offset.local.value); | 4528 | try func.addLabel(.local_set, new_len.local.value); |
| 4553 | try func.addLabel(.br, 0); // jump to start of loop | 4529 | break :blk new_len; |
| 4554 | try func.endBlock(); | 4530 | } else len, |
| 4555 | try func.endBlock(); | 4531 | }; |
| | 4532 | |
| | 4533 | var end_ptr = try func.allocLocal(Type.usize); |
| | 4534 | defer end_ptr.free(func); |
| | 4535 | var new_ptr = try func.buildPointerOffset(ptr, 0, .new); |
| | 4536 | defer new_ptr.free(func); |
| | 4537 | |
| | 4538 | // get the loop conditional: if current pointer address equals final pointer's address |
| | 4539 | try func.lowerToStack(ptr); |
| | 4540 | try func.emitWValue(final_len); |
| | 4541 | switch (func.arch()) { |
| | 4542 | .wasm32 => try func.addTag(.i32_add), |
| | 4543 | .wasm64 => try func.addTag(.i64_add), |
| | 4544 | else => unreachable, |
| | 4545 | } |
| | 4546 | try func.addLabel(.local_set, end_ptr.local.value); |
| | 4547 | |
| | 4548 | // outer block to jump to when loop is done |
| | 4549 | try func.startBlock(.block, wasm.block_empty); |
| | 4550 | try func.startBlock(.loop, wasm.block_empty); |
| | 4551 | |
| | 4552 | // check for codition for loop end |
| | 4553 | try func.emitWValue(new_ptr); |
| | 4554 | try func.emitWValue(end_ptr); |
| | 4555 | switch (func.arch()) { |
| | 4556 | .wasm32 => try func.addTag(.i32_eq), |
| | 4557 | .wasm64 => try func.addTag(.i64_eq), |
| | 4558 | else => unreachable, |
| | 4559 | } |
| | 4560 | try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished) |
| | 4561 | |
| | 4562 | // store the value at the current position of the pointer |
| | 4563 | try func.store(new_ptr, value, elem_ty, 0); |
| | 4564 | |
| | 4565 | // move the pointer to the next element |
| | 4566 | try func.emitWValue(new_ptr); |
| | 4567 | switch (func.arch()) { |
| | 4568 | .wasm32 => { |
| | 4569 | try func.emitWValue(.{ .imm32 = abi_size }); |
| | 4570 | try func.addTag(.i32_add); |
| 4556 | }, | 4571 | }, |
| | 4572 | .wasm64 => { |
| | 4573 | try func.emitWValue(.{ .imm64 = abi_size }); |
| | 4574 | try func.addTag(.i64_add); |
| | 4575 | }, |
| | 4576 | else => unreachable, |
| 4557 | } | 4577 | } |
| | 4578 | try func.addLabel(.local_set, new_ptr.local.value); |
| | 4579 | |
| | 4580 | // end of loop |
| | 4581 | try func.addLabel(.br, 0); // jump to start of loop |
| | 4582 | try func.endBlock(); |
| | 4583 | try func.endBlock(); |
| 4558 | } | 4584 | } |
| 4559 | | 4585 | |
| 4560 | fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4586 | fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | @@ -6007,10 +6033,12 @@ fn lowerTry( | ... | @@ -6007,10 +6033,12 @@ fn lowerTry( |
| 6007 | const liveness = func.liveness.getCondBr(inst); | 6033 | const liveness = func.liveness.getCondBr(inst); |
| 6008 | try func.branches.append(func.gpa, .{}); | 6034 | try func.branches.append(func.gpa, .{}); |
| 6009 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.else_deaths.len + liveness.then_deaths.len); | 6035 | try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.else_deaths.len + liveness.then_deaths.len); |
| | 6036 | defer { |
| | 6037 | var branch = func.branches.pop(); |
| | 6038 | branch.deinit(func.gpa); |
| | 6039 | } |
| 6010 | try func.genBody(body); | 6040 | try func.genBody(body); |
| 6011 | try func.endBlock(); | 6041 | try func.endBlock(); |
| 6012 | var branch = func.branches.pop(); | | |
| 6013 | branch.deinit(func.gpa); | | |
| 6014 | } | 6042 | } |
| 6015 | | 6043 | |
| 6016 | // if we reach here it means error was not set, and we want the payload | 6044 | // if we reach here it means error was not set, and we want the payload |