authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-31 21:12:30+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-02-03 21:43:25+01:00
logae1e3c8f9bc86eeefb5a83233884a134f7b974f4
treeed25223280b2ccfffd74691b73e558065ec7925d
parent29013220d95f60669c4a181d157157aea9f137b5
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Implement vector_init for array & structs

Implements the instruction `vector_init` for structs and arrays. For arrays, it checks if the element must be passed by reference or not. When not, it can simply use the `offset` field of a store instruction to copy the values into the array. When it is byref, it will move the pointer by the element size, and then perform a store operation. This ensures types like structs will be moved into the right position. For structs we will always move the pointer, as we currently cannot verify if all fields are not by ref.

1 files changed, 89 insertions(+), 27 deletions(-)

src/arch/wasm/CodeGen.zig+89-27
......@@ -44,7 +44,7 @@ const WValue = union(enum) {
4444 memory_offset: struct {
4545 /// The symbol of the parent pointer
4646 pointer: u32,
47 /// Offset will be set as 'addend' when relocating
47 /// Offset will be set as addend when relocating
4848 offset: u32,
4949 },
5050 /// Represents a function pointer
......@@ -606,7 +606,10 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
606606 // means we must generate it from a constant.
607607 const val = self.air.value(ref).?;
608608 const ty = self.air.typeOf(ref);
609 if (!ty.hasRuntimeBits() and !ty.isInt()) return WValue{ .none = {} };
609 if (!ty.hasRuntimeBits() and !ty.isInt()) {
610 gop.value_ptr.* = WValue{ .none = {} };
611 return gop.value_ptr.*;
612 }
610613
611614 // When we need to pass the value by reference (such as a struct), we will
612615 // leverage `genTypedValue` to lower the constant to bytes and emit it
......@@ -1644,6 +1647,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
16441647fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16451648 const un_op = self.air.instructions.items(.data)[inst].un_op;
16461649 const operand = try self.resolveInst(un_op);
1650
16471651 // result must be stored in the stack and we return a pointer
16481652 // to the stack instead
16491653 if (self.return_value != .none) {
......@@ -1653,7 +1657,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16531657 }
16541658 try self.restoreStackPointer();
16551659 try self.addTag(.@"return");
1656 return .none;
1660 return WValue{ .none = {} };
16571661}
16581662
16591663fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -1793,11 +1797,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
17931797 const err_ty = ty.errorUnionSet();
17941798 const pl_ty = ty.errorUnionPayload();
17951799 if (!pl_ty.hasRuntimeBits()) {
1796 const err_val = try self.load(rhs, err_ty, 0);
1797 return self.store(lhs, err_val, err_ty, 0);
1800 return self.store(lhs, rhs, err_ty, 0);
17981801 }
17991802
1800 return try self.memCopy(ty, lhs, rhs);
1803 return self.memCopy(ty, lhs, rhs);
18011804 },
18021805 .Optional => {
18031806 if (ty.isPtrLikeOptional()) {
......@@ -1812,7 +1815,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
18121815 return self.memCopy(ty, lhs, rhs);
18131816 },
18141817 .Struct, .Array, .Union => {
1815 return try self.memCopy(ty, lhs, rhs);
1818 return self.memCopy(ty, lhs, rhs);
18161819 },
18171820 .Pointer => {
18181821 if (ty.isSlice()) {
......@@ -1827,7 +1830,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
18271830 }
18281831 },
18291832 .Int => if (ty.intInfo(self.target).bits > 64) {
1830 return try self.memCopy(ty, lhs, rhs);
1833 return self.memCopy(ty, lhs, rhs);
18311834 },
18321835 else => {},
18331836 }
......@@ -2587,11 +2590,11 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue
25872590 if (isByRef(payload_ty, self.target)) {
25882591 return self.buildPointerOffset(operand, offset, .new);
25892592 }
2590 return try self.load(operand, payload_ty, offset);
2593 return self.load(operand, payload_ty, offset);
25912594}
25922595
25932596fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2594 if (self.liveness.isUnused(inst)) return WValue.none;
2597 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
25952598
25962599 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
25972600 const operand = try self.resolveInst(ty_op.operand);
......@@ -2601,11 +2604,12 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26012604 return operand;
26022605 }
26032606
2604 return try self.load(operand, err_ty.errorUnionSet(), 0);
2607 return self.load(operand, err_ty.errorUnionSet(), 0);
26052608}
26062609
26072610fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2608 if (self.liveness.isUnused(inst)) return WValue.none;
2611 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2612
26092613 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
26102614 const operand = try self.resolveInst(ty_op.operand);
26112615
......@@ -2627,11 +2631,14 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26272631}
26282632
26292633fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2630 if (self.liveness.isUnused(inst)) return WValue.none;
2634 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2635
26312636 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
26322637 const operand = try self.resolveInst(ty_op.operand);
26332638 const err_ty = self.air.getRefType(ty_op.ty);
26342639
2640 if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand;
2641
26352642 const err_union = try self.allocStack(err_ty);
26362643 // TODO: Also write 'undefined' to the payload
26372644 try self.store(err_union, operand, err_ty.errorUnionSet(), 0);
......@@ -2813,16 +2820,16 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28132820}
28142821
28152822fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2816 if (self.liveness.isUnused(inst)) return WValue.none;
2823 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
28172824
28182825 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
28192826 const operand = try self.resolveInst(ty_op.operand);
28202827
2821 return try self.load(operand, Type.usize, self.ptrSize());
2828 return self.load(operand, Type.usize, self.ptrSize());
28222829}
28232830
28242831fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2825 if (self.liveness.isUnused(inst)) return WValue.none;
2832 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
28262833
28272834 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
28282835 const slice_ty = self.air.typeOf(bin_op.lhs);
......@@ -2847,7 +2854,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28472854 if (isByRef(elem_ty, self.target)) {
28482855 return result;
28492856 }
2850 return try self.load(result, elem_ty, 0);
2857 return self.load(result, elem_ty, 0);
28512858}
28522859
28532860fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2875,10 +2882,10 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28752882}
28762883
28772884fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2878 if (self.liveness.isUnused(inst)) return WValue.none;
2885 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
28792886 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
28802887 const operand = try self.resolveInst(ty_op.operand);
2881 return try self.load(operand, Type.usize, 0);
2888 return self.load(operand, Type.usize, 0);
28822889}
28832890
28842891fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2943,7 +2950,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29432950
29442951fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29452952 const un_op = self.air.instructions.items(.data)[inst].un_op;
2946 return try self.resolveInst(un_op);
2953 return self.resolveInst(un_op);
29472954}
29482955
29492956fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2975,7 +2982,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29752982fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29762983 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
29772984 const un_op = self.air.instructions.items(.data)[inst].un_op;
2978 return try self.resolveInst(un_op);
2985 return self.resolveInst(un_op);
29792986}
29802987
29812988fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2990,7 +2997,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29902997
29912998 // load pointer onto the stack
29922999 if (ptr_ty.isSlice()) {
2993 const ptr_local = try self.load(pointer, ptr_ty, 0);
3000 const ptr_local = try self.load(pointer, Type.usize, 0);
29943001 try self.addLabel(.local_get, ptr_local.local);
29953002 } else {
29963003 try self.emitWValue(pointer);
......@@ -3007,7 +3014,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30073014 if (isByRef(elem_ty, self.target)) {
30083015 return result;
30093016 }
3010 return try self.load(result, elem_ty, 0);
3017 return self.load(result, elem_ty, 0);
30113018}
30123019
30133020fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3023,7 +3030,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30233030
30243031 // load pointer onto the stack
30253032 if (ptr_ty.isSlice()) {
3026 const ptr_local = try self.load(ptr, ptr_ty, 0);
3033 const ptr_local = try self.load(ptr, Type.usize, 0);
30273034 try self.addLabel(.local_get, ptr_local.local);
30283035 } else {
30293036 try self.emitWValue(ptr);
......@@ -3157,7 +3164,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
31573164 if (isByRef(elem_ty, self.target)) {
31583165 return result;
31593166 }
3160 return try self.load(result, elem_ty, 0);
3167 return self.load(result, elem_ty, 0);
31613168}
31623169
31633170fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3201,8 +3208,63 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
32013208 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
32023209 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
32033210
3204 _ = elements;
3205 return self.fail("TODO: Wasm backend: implement airVectorInit", .{});
3211 switch (vector_ty.zigTypeTag()) {
3212 .Vector => return self.fail("TODO: Wasm backend: implement airVectorInit for vectors", .{}),
3213 .Array => {
3214 const result = try self.allocStack(vector_ty);
3215 const elem_ty = vector_ty.childType();
3216 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
3217
3218 // When the element type is by reference, we must copy the entire
3219 // value. It is therefore safer to move the offset pointer and store
3220 // each value individually, instead of using store offsets.
3221 if (isByRef(elem_ty, self.target)) {
3222 // copy stack pointer into a temporary local, which is
3223 // moved for each element to store each value in the right position.
3224 const offset = try self.allocLocal(Type.usize);
3225 try self.emitWValue(result);
3226 try self.addLabel(.local_set, offset.local);
3227 for (elements) |elem, elem_index| {
3228 const elem_val = try self.resolveInst(elem);
3229 try self.store(offset, elem_val, elem_ty, 0);
3230
3231 if (elem_index < elements.len - 1) {
3232 _ = try self.buildPointerOffset(offset, elem_size, .modify);
3233 }
3234 }
3235 } else {
3236 var offset: u32 = 0;
3237 for (elements) |elem| {
3238 const elem_val = try self.resolveInst(elem);
3239 try self.store(result, elem_val, elem_ty, offset);
3240 offset += elem_size;
3241 }
3242 }
3243 return result;
3244 },
3245 .Struct => {
3246 const tuple = vector_ty.castTag(.tuple).?.data;
3247 const result = try self.allocStack(vector_ty);
3248 const offset = try self.allocLocal(Type.usize); // pointer to offset
3249 try self.emitWValue(result);
3250 try self.addLabel(.local_set, offset.local);
3251 for (elements) |elem, elem_index| {
3252 if (tuple.values[elem_index].tag() != .unreachable_value) continue;
3253
3254 const elem_ty = tuple.types[elem_index];
3255 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
3256 const value = try self.resolveInst(elem);
3257 try self.store(offset, value, elem_ty, 0);
3258
3259 if (elem_index < elements.len - 1) {
3260 _ = try self.buildPointerOffset(offset, elem_size, .modify);
3261 }
3262 }
3263
3264 return result;
3265 },
3266 else => unreachable,
3267 }
32063268}
32073269
32083270fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {