| ... | ... | @@ -1605,10 +1605,16 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { |
| 1605 | 1605 | else => {}, |
| 1606 | 1606 | } |
| 1607 | 1607 | |
| 1608 | | // TODO: We should probably lower this to a call to compiler_rt |
| 1609 | | // But for now, we implement it manually |
| 1610 | | var offset = try func.ensureAllocLocal(Type.usize); // local for counter |
| 1608 | // allocate a local for the offset, and set it to 0. |
| 1609 | // This to ensure that inside loops we correctly re-set the counter. |
| 1610 | var offset = try func.allocLocal(Type.usize); // local for counter |
| 1611 | 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 | 1619 | // outer block to jump to when loop is done |
| 1614 | 1620 | try func.startBlock(.block, wasm.block_empty); |
| ... | ... | @@ -3301,19 +3307,23 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3301 | 3307 | { |
| 3302 | 3308 | func.branches.appendAssumeCapacity(.{}); |
| 3303 | 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 | 3314 | try func.genBody(else_body); |
| 3305 | 3315 | try func.endBlock(); |
| 3306 | | var else_stack = func.branches.pop(); |
| 3307 | | else_stack.deinit(func.gpa); |
| 3308 | 3316 | } |
| 3309 | 3317 | |
| 3310 | 3318 | // Outer block that matches the condition |
| 3311 | 3319 | { |
| 3312 | 3320 | func.branches.appendAssumeCapacity(.{}); |
| 3313 | 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 | 3326 | try func.genBody(then_body); |
| 3315 | | var then_stack = func.branches.pop(); |
| 3316 | | then_stack.deinit(func.gpa); |
| 3317 | 3327 | } |
| 3318 | 3328 | |
| 3319 | 3329 | func.finishAir(inst, .none, &.{}); |
| ... | ... | @@ -3829,20 +3839,24 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3829 | 3839 | } |
| 3830 | 3840 | func.branches.appendAssumeCapacity(.{}); |
| 3831 | 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 | 3846 | try func.genBody(case.body); |
| 3833 | 3847 | try func.endBlock(); |
| 3834 | | var case_branch = func.branches.pop(); |
| 3835 | | case_branch.deinit(func.gpa); |
| 3836 | 3848 | } |
| 3837 | 3849 | |
| 3838 | 3850 | if (has_else_body) { |
| 3839 | 3851 | func.branches.appendAssumeCapacity(.{}); |
| 3840 | 3852 | const else_deaths = liveness.deaths.len - 1; |
| 3841 | 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 | 3858 | try func.genBody(else_body); |
| 3843 | 3859 | try func.endBlock(); |
| 3844 | | var else_branch = func.branches.pop(); |
| 3845 | | else_branch.deinit(func.gpa); |
| 3846 | 3860 | } |
| 3847 | 3861 | func.finishAir(inst, .none, &.{}); |
| 3848 | 3862 | } |
| ... | ... | @@ -3971,7 +3985,7 @@ fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3971 | 3985 | // write 'undefined' to the payload |
| 3972 | 3986 | const payload_ptr = try func.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, func.target)), .new); |
| 3973 | 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 | 3990 | break :result err_union; |
| 3977 | 3991 | }; |
| ... | ... | @@ -4466,8 +4480,13 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void |
| 4466 | 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 | 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 | 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 | 4495 | /// When the user has enabled the bulk_memory feature, we lower |
| 4477 | 4496 | /// this to wasm's memset instruction. When the feature is not present, |
| 4478 | 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 | 4501 | // When bulk_memory is enabled, we lower it to wasm's memset instruction. |
| 4481 | | // If not, we lower it ourselves |
| 4482 | | if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory)) { |
| 4502 | // If not, we lower it ourselves. |
| 4503 | if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory) and abi_size == 1) { |
| 4483 | 4504 | try func.lowerToStack(ptr); |
| 4484 | 4505 | try func.emitWValue(value); |
| 4485 | 4506 | try func.emitWValue(len); |
| ... | ... | @@ -4487,74 +4508,79 @@ fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!vo |
| 4487 | 4508 | return; |
| 4488 | 4509 | } |
| 4489 | 4510 | |
| 4490 | | // When the length is comptime-known we do the loop at codegen, rather |
| 4491 | | // than emitting a runtime loop into the binary |
| 4492 | | switch (len) { |
| 4493 | | .imm32, .imm64 => { |
| 4494 | | const length = switch (len) { |
| 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); |
| 4511 | const final_len = switch (len) { |
| 4512 | .imm32 => |val| WValue{ .imm32 = val * abi_size }, |
| 4513 | .imm64 => |val| WValue{ .imm64 = val * abi_size }, |
| 4514 | else => if (abi_size != 1) blk: { |
| 4515 | const new_len = try func.ensureAllocLocal(Type.usize); |
| 4524 | 4516 | try func.emitWValue(len); |
| 4525 | 4517 | switch (func.arch()) { |
| 4526 | | .wasm32 => try func.addTag(.i32_eq), |
| 4527 | | .wasm64 => try func.addTag(.i64_eq), |
| 4528 | | else => unreachable, |
| 4529 | | } |
| 4530 | | try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished) |
| 4531 | | try func.emitWValue(ptr); |
| 4532 | | try func.emitWValue(offset); |
| 4533 | | switch (func.arch()) { |
| 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), |
| 4518 | .wasm32 => { |
| 4519 | try func.emitWValue(.{ .imm32 = abi_size }); |
| 4520 | try func.addTag(.i32_mul); |
| 4521 | }, |
| 4522 | .wasm64 => { |
| 4523 | try func.emitWValue(.{ .imm64 = abi_size }); |
| 4524 | try func.addTag(.i64_mul); |
| 4525 | }, |
| 4550 | 4526 | else => unreachable, |
| 4551 | 4527 | } |
| 4552 | | try func.addLabel(.local_set, offset.local.value); |
| 4553 | | try func.addLabel(.br, 0); // jump to start of loop |
| 4554 | | try func.endBlock(); |
| 4555 | | try func.endBlock(); |
| 4528 | try func.addLabel(.local_set, new_len.local.value); |
| 4529 | break :blk new_len; |
| 4530 | } else len, |
| 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 | 4586 | fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -6007,10 +6033,12 @@ fn lowerTry( |
| 6007 | 6033 | const liveness = func.liveness.getCondBr(inst); |
| 6008 | 6034 | try func.branches.append(func.gpa, .{}); |
| 6009 | 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 | 6040 | try func.genBody(body); |
| 6011 | 6041 | try func.endBlock(); |
| 6012 | | var branch = func.branches.pop(); |
| 6013 | | branch.deinit(func.gpa); |
| 6014 | 6042 | } |
| 6015 | 6043 | |
| 6016 | 6044 | // if we reach here it means error was not set, and we want the payload |