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 {...@@ -2519,8 +2519,16 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2519 if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand;2519 if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand;
25202520
2521 const err_union = try self.allocStack(err_ty);2521 const err_union = try self.allocStack(err_ty);
2522 // TODO: Also write 'undefined' to the payload
2523 try self.store(err_union, operand, err_ty.errorUnionSet(), 0);2522 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
2524 return err_union;2532 return err_union;
2525}2533}
25262534
...@@ -2972,7 +2980,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2972,7 +2980,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2972 const ptr = try self.resolveInst(pl_op.operand);2980 const ptr = try self.resolveInst(pl_op.operand);
2973 const value = try self.resolveInst(bin_op.lhs);2981 const value = try self.resolveInst(bin_op.lhs);
2974 const len = try self.resolveInst(bin_op.rhs);2982 const len = try self.resolveInst(bin_op.rhs);
2975 try self.memSet(ptr, len, value);2983 try self.memset(ptr, len, value);
29762984
2977 return WValue{ .none = {} };2985 return WValue{ .none = {} };
2978}2986}
...@@ -2981,7 +2989,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2981,7 +2989,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2981/// When the user has enabled the bulk_memory feature, we lower2989/// When the user has enabled the bulk_memory feature, we lower
2982/// this to wasm's memset instruction. When the feature is not present,2990/// this to wasm's memset instruction. When the feature is not present,
2983/// we implement it manually.2991/// 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 {
2985 // When bulk_memory is enabled, we lower it to wasm's memset instruction.2993 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
2986 // If not, we lower it ourselves2994 // If not, we lower it ourselves
2987 if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) {2995 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...@@ -2995,45 +3003,74 @@ fn memSet(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void
2995 return;3003 return;
2996 }3004 }
29973005
2998 // TODO: We should probably lower this to a call to compiler_rt3006 // When the length is comptime-known we do the loop at codegen, rather
2999 // But for now, we implement it manually3007 // than emitting a runtime loop into the binary
3000 const offset = try self.allocLocal(Type.usize); // local for counter3008 switch (len) {
3001 // outer block to jump to when loop is done3009 .imm32, .imm64 => {
3002 try self.startBlock(.block, wasm.block_empty);3010 const length = switch (len) {
3003 try self.startBlock(.loop, wasm.block_empty);3011 .imm32 => |val| val,
3004 try self.emitWValue(offset);3012 .imm64 => |val| val,
3005 try self.emitWValue(len);3013 else => unreachable,
3006 switch (self.ptrSize()) {3014 };
3007 4 => try self.addTag(.i32_eq),3015
3008 8 => try self.addTag(.i64_eq),3016 var offset: u32 = 0;
3009 else => unreachable,3017 const base = ptr.offset();
3010 }3018 while (offset < length) : (offset += 1) {
3011 try self.addLabel(.br_if, 1); // jump out of loop into outer block (finished)3019 try self.emitWValue(ptr);
3012 try self.emitWValue(ptr);3020 try self.emitWValue(value);
3013 try self.emitWValue(offset);3021 switch (self.arch()) {
3014 switch (self.arch()) {3022 .wasm32 => {
3015 .wasm32 => try self.addTag(.i32_add),3023 try self.addMemArg(.i32_store8, .{ .offset = base + offset, .alignment = 1 });
3016 .wasm64 => try self.addTag(.i64_add),3024 },
3017 else => unreachable,3025 .wasm64 => {
3018 }3026 try self.addMemArg(.i64_store8, .{ .offset = base + offset, .alignment = 1 });
3019 try self.emitWValue(value);3027 },
3020 const mem_store_op: Mir.Inst.Tag = switch (self.arch()) {3028 else => unreachable,
3021 .wasm32 => .i32_store8,3029 }
3022 .wasm64 => .i64_store8,3030 }
3023 else => unreachable,3031 },
3024 };3032 else => {
3025 try self.addMemArg(mem_store_op, .{ .offset = ptr.offset(), .alignment = 1 });3033 // TODO: We should probably lower this to a call to compiler_rt
3026 try self.emitWValue(offset);3034 // But for now, we implement it manually
3027 try self.addImm32(1);3035 const offset = try self.allocLocal(Type.usize); // local for counter
3028 switch (self.ptrSize()) {3036 // outer block to jump to when loop is done
3029 4 => try self.addTag(.i32_add),3037 try self.startBlock(.block, wasm.block_empty);
3030 8 => try self.addTag(.i64_add),3038 try self.startBlock(.loop, wasm.block_empty);
3031 else => unreachable,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 },
3032 }3073 }
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();
3037}3074}
30383075
3039fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3076fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {