authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-03-16 22:08:25+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-03-17 20:41:26+01:00
log215a22541c2a5b9886173dca86e2990ae5c649d1
tree491fce66ba10538c36b3030351f686ca1cb98bb4
parenteafdc5562f5053ecc193041e83d3661ef0744ebb
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Improve memset implementation

When the length is comptime-known, we perform an inline loop instead of emitting a runtime loop into the binary. This also allows us to easily write 'undefined' to aggregate types. We now do this when we set the error tag of an error union where the payload will be set to undefined.

1 files changed, 78 insertions(+), 41 deletions(-)

src/arch/wasm/CodeGen.zig+78-41
......@@ -2519,8 +2519,16 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
25192519 if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand;
25202520
25212521 const err_union = try self.allocStack(err_ty);
2522 // TODO: Also write 'undefined' to the payload
25232522 try self.store(err_union, operand, err_ty.errorUnionSet(), 0);
2523
2524 // write 'undefined' to the payload
2525 const err_align = err_ty.abiAlignment(self.target);
2526 const set_size = err_ty.errorUnionSet().abiSize(self.target);
2527 const offset = mem.alignForwardGeneric(u64, set_size, err_align);
2528 const payload_ptr = try self.buildPointerOffset(err_union, offset, .new);
2529 const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(self.target));
2530 try self.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa });
2531
25242532 return err_union;
25252533}
25262534
......@@ -2972,7 +2980,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29722980 const ptr = try self.resolveInst(pl_op.operand);
29732981 const value = try self.resolveInst(bin_op.lhs);
29742982 const len = try self.resolveInst(bin_op.rhs);
2975 try self.memSet(ptr, len, value);
2983 try self.memset(ptr, len, value);
29762984
29772985 return WValue{ .none = {} };
29782986}
......@@ -2981,7 +2989,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29812989/// When the user has enabled the bulk_memory feature, we lower
29822990/// this to wasm's memset instruction. When the feature is not present,
29832991/// we implement it manually.
2984fn memSet(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void {
2992fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void {
29852993 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
29862994 // If not, we lower it ourselves
29872995 if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) {
......@@ -2995,45 +3003,74 @@ fn memSet(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void
29953003 return;
29963004 }
29973005
2998 // TODO: We should probably lower this to a call to compiler_rt
2999 // But for now, we implement it manually
3000 const offset = try self.allocLocal(Type.usize); // local for counter
3001 // outer block to jump to when loop is done
3002 try self.startBlock(.block, wasm.block_empty);
3003 try self.startBlock(.loop, wasm.block_empty);
3004 try self.emitWValue(offset);
3005 try self.emitWValue(len);
3006 switch (self.ptrSize()) {
3007 4 => try self.addTag(.i32_eq),
3008 8 => try self.addTag(.i64_eq),
3009 else => unreachable,
3010 }
3011 try self.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
3012 try self.emitWValue(ptr);
3013 try self.emitWValue(offset);
3014 switch (self.arch()) {
3015 .wasm32 => try self.addTag(.i32_add),
3016 .wasm64 => try self.addTag(.i64_add),
3017 else => unreachable,
3018 }
3019 try self.emitWValue(value);
3020 const mem_store_op: Mir.Inst.Tag = switch (self.arch()) {
3021 .wasm32 => .i32_store8,
3022 .wasm64 => .i64_store8,
3023 else => unreachable,
3024 };
3025 try self.addMemArg(mem_store_op, .{ .offset = ptr.offset(), .alignment = 1 });
3026 try self.emitWValue(offset);
3027 try self.addImm32(1);
3028 switch (self.ptrSize()) {
3029 4 => try self.addTag(.i32_add),
3030 8 => try self.addTag(.i64_add),
3031 else => unreachable,
3006 // When the length is comptime-known we do the loop at codegen, rather
3007 // than emitting a runtime loop into the binary
3008 switch (len) {
3009 .imm32, .imm64 => {
3010 const length = switch (len) {
3011 .imm32 => |val| val,
3012 .imm64 => |val| val,
3013 else => unreachable,
3014 };
3015
3016 var offset: u32 = 0;
3017 const base = ptr.offset();
3018 while (offset < length) : (offset += 1) {
3019 try self.emitWValue(ptr);
3020 try self.emitWValue(value);
3021 switch (self.arch()) {
3022 .wasm32 => {
3023 try self.addMemArg(.i32_store8, .{ .offset = base + offset, .alignment = 1 });
3024 },
3025 .wasm64 => {
3026 try self.addMemArg(.i64_store8, .{ .offset = base + offset, .alignment = 1 });
3027 },
3028 else => unreachable,
3029 }
3030 }
3031 },
3032 else => {
3033 // TODO: We should probably lower this to a call to compiler_rt
3034 // But for now, we implement it manually
3035 const offset = try self.allocLocal(Type.usize); // local for counter
3036 // outer block to jump to when loop is done
3037 try self.startBlock(.block, wasm.block_empty);
3038 try self.startBlock(.loop, wasm.block_empty);
3039 try self.emitWValue(offset);
3040 try self.emitWValue(len);
3041 switch (self.arch()) {
3042 .wasm32 => try self.addTag(.i32_eq),
3043 .wasm64 => try self.addTag(.i64_eq),
3044 else => unreachable,
3045 }
3046 try self.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
3047 try self.emitWValue(ptr);
3048 try self.emitWValue(offset);
3049 switch (self.arch()) {
3050 .wasm32 => try self.addTag(.i32_add),
3051 .wasm64 => try self.addTag(.i64_add),
3052 else => unreachable,
3053 }
3054 try self.emitWValue(value);
3055 const mem_store_op: Mir.Inst.Tag = switch (self.arch()) {
3056 .wasm32 => .i32_store8,
3057 .wasm64 => .i64_store8,
3058 else => unreachable,
3059 };
3060 try self.addMemArg(mem_store_op, .{ .offset = ptr.offset(), .alignment = 1 });
3061 try self.emitWValue(offset);
3062 try self.addImm32(1);
3063 switch (self.arch()) {
3064 .wasm32 => try self.addTag(.i32_add),
3065 .wasm64 => try self.addTag(.i64_add),
3066 else => unreachable,
3067 }
3068 try self.addLabel(.local_set, offset.local);
3069 try self.addLabel(.br, 0); // jump to start of loop
3070 try self.endBlock();
3071 try self.endBlock();
3072 },
30323073 }
3033 try self.addLabel(.local_set, offset.local);
3034 try self.addLabel(.br, 0); // jump to start of loop
3035 try self.endBlock();
3036 try self.endBlock();
30373074}
30383075
30393076fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {