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) {...@@ -44,7 +44,7 @@ const WValue = union(enum) {
44 memory_offset: struct {44 memory_offset: struct {
45 /// The symbol of the parent pointer45 /// The symbol of the parent pointer
46 pointer: u32,46 pointer: u32,
47 /// Offset will be set as 'addend' when relocating47 /// Offset will be set as addend when relocating
48 offset: u32,48 offset: u32,
49 },49 },
50 /// Represents a function pointer50 /// Represents a function pointer
...@@ -606,7 +606,10 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {...@@ -606,7 +606,10 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
606 // means we must generate it from a constant.606 // means we must generate it from a constant.
607 const val = self.air.value(ref).?;607 const val = self.air.value(ref).?;
608 const ty = self.air.typeOf(ref);608 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
611 // When we need to pass the value by reference (such as a struct), we will614 // When we need to pass the value by reference (such as a struct), we will
612 // leverage `genTypedValue` to lower the constant to bytes and emit it615 // 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 {...@@ -1644,6 +1647,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1644fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1647fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1645 const un_op = self.air.instructions.items(.data)[inst].un_op;1648 const un_op = self.air.instructions.items(.data)[inst].un_op;
1646 const operand = try self.resolveInst(un_op);1649 const operand = try self.resolveInst(un_op);
1650
1647 // result must be stored in the stack and we return a pointer1651 // result must be stored in the stack and we return a pointer
1648 // to the stack instead1652 // to the stack instead
1649 if (self.return_value != .none) {1653 if (self.return_value != .none) {
...@@ -1653,7 +1657,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1653,7 +1657,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1653 }1657 }
1654 try self.restoreStackPointer();1658 try self.restoreStackPointer();
1655 try self.addTag(.@"return");1659 try self.addTag(.@"return");
1656 return .none;1660 return WValue{ .none = {} };
1657}1661}
16581662
1659fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1663fn 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...@@ -1793,11 +1797,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1793 const err_ty = ty.errorUnionSet();1797 const err_ty = ty.errorUnionSet();
1794 const pl_ty = ty.errorUnionPayload();1798 const pl_ty = ty.errorUnionPayload();
1795 if (!pl_ty.hasRuntimeBits()) {1799 if (!pl_ty.hasRuntimeBits()) {
1796 const err_val = try self.load(rhs, err_ty, 0);1800 return self.store(lhs, rhs, err_ty, 0);
1797 return self.store(lhs, err_val, err_ty, 0);
1798 }1801 }
17991802
1800 return try self.memCopy(ty, lhs, rhs);1803 return self.memCopy(ty, lhs, rhs);
1801 },1804 },
1802 .Optional => {1805 .Optional => {
1803 if (ty.isPtrLikeOptional()) {1806 if (ty.isPtrLikeOptional()) {
...@@ -1812,7 +1815,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1812,7 +1815,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1812 return self.memCopy(ty, lhs, rhs);1815 return self.memCopy(ty, lhs, rhs);
1813 },1816 },
1814 .Struct, .Array, .Union => {1817 .Struct, .Array, .Union => {
1815 return try self.memCopy(ty, lhs, rhs);1818 return self.memCopy(ty, lhs, rhs);
1816 },1819 },
1817 .Pointer => {1820 .Pointer => {
1818 if (ty.isSlice()) {1821 if (ty.isSlice()) {
...@@ -1827,7 +1830,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1827,7 +1830,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1827 }1830 }
1828 },1831 },
1829 .Int => if (ty.intInfo(self.target).bits > 64) {1832 .Int => if (ty.intInfo(self.target).bits > 64) {
1830 return try self.memCopy(ty, lhs, rhs);1833 return self.memCopy(ty, lhs, rhs);
1831 },1834 },
1832 else => {},1835 else => {},
1833 }1836 }
...@@ -2587,11 +2590,11 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue...@@ -2587,11 +2590,11 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue
2587 if (isByRef(payload_ty, self.target)) {2590 if (isByRef(payload_ty, self.target)) {
2588 return self.buildPointerOffset(operand, offset, .new);2591 return self.buildPointerOffset(operand, offset, .new);
2589 }2592 }
2590 return try self.load(operand, payload_ty, offset);2593 return self.load(operand, payload_ty, offset);
2591}2594}
25922595
2593fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2596fn 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
2596 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2599 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2597 const operand = try self.resolveInst(ty_op.operand);2600 const operand = try self.resolveInst(ty_op.operand);
...@@ -2601,11 +2604,12 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2601,11 +2604,12 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2601 return operand;2604 return operand;
2602 }2605 }
26032606
2604 return try self.load(operand, err_ty.errorUnionSet(), 0);2607 return self.load(operand, err_ty.errorUnionSet(), 0);
2605}2608}
26062609
2607fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2610fn 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
2609 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2613 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2610 const operand = try self.resolveInst(ty_op.operand);2614 const operand = try self.resolveInst(ty_op.operand);
26112615
...@@ -2627,11 +2631,14 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2627,11 +2631,14 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2627}2631}
26282632
2629fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2633fn 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
2631 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2636 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2632 const operand = try self.resolveInst(ty_op.operand);2637 const operand = try self.resolveInst(ty_op.operand);
2633 const err_ty = self.air.getRefType(ty_op.ty);2638 const err_ty = self.air.getRefType(ty_op.ty);
26342639
2640 if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand;
2641
2635 const err_union = try self.allocStack(err_ty);2642 const err_union = try self.allocStack(err_ty);
2636 // TODO: Also write 'undefined' to the payload2643 // TODO: Also write 'undefined' to the payload
2637 try self.store(err_union, operand, err_ty.errorUnionSet(), 0);2644 try self.store(err_union, operand, err_ty.errorUnionSet(), 0);
...@@ -2813,16 +2820,16 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2813,16 +2820,16 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2813}2820}
28142821
2815fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2822fn 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
2818 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2825 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2819 const operand = try self.resolveInst(ty_op.operand);2826 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());
2822}2829}
28232830
2824fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2831fn 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
2827 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2834 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2828 const slice_ty = self.air.typeOf(bin_op.lhs);2835 const slice_ty = self.air.typeOf(bin_op.lhs);
...@@ -2847,7 +2854,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2847,7 +2854,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2847 if (isByRef(elem_ty, self.target)) {2854 if (isByRef(elem_ty, self.target)) {
2848 return result;2855 return result;
2849 }2856 }
2850 return try self.load(result, elem_ty, 0);2857 return self.load(result, elem_ty, 0);
2851}2858}
28522859
2853fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2860fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2875,10 +2882,10 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2875,10 +2882,10 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2875}2882}
28762883
2877fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2884fn 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 = {} };
2879 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2886 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2880 const operand = try self.resolveInst(ty_op.operand);2887 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);
2882}2889}
28832890
2884fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2891fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2943,7 +2950,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2943,7 +2950,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29432950
2944fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2951fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2945 const un_op = self.air.instructions.items(.data)[inst].un_op;2952 const un_op = self.air.instructions.items(.data)[inst].un_op;
2946 return try self.resolveInst(un_op);2953 return self.resolveInst(un_op);
2947}2954}
29482955
2949fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2956fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2975,7 +2982,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2975,7 +2982,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2975fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2982fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2976 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };2983 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2977 const un_op = self.air.instructions.items(.data)[inst].un_op;2984 const un_op = self.air.instructions.items(.data)[inst].un_op;
2978 return try self.resolveInst(un_op);2985 return self.resolveInst(un_op);
2979}2986}
29802987
2981fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2988fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2990,7 +2997,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2990,7 +2997,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29902997
2991 // load pointer onto the stack2998 // load pointer onto the stack
2992 if (ptr_ty.isSlice()) {2999 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);
2994 try self.addLabel(.local_get, ptr_local.local);3001 try self.addLabel(.local_get, ptr_local.local);
2995 } else {3002 } else {
2996 try self.emitWValue(pointer);3003 try self.emitWValue(pointer);
...@@ -3007,7 +3014,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3007,7 +3014,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3007 if (isByRef(elem_ty, self.target)) {3014 if (isByRef(elem_ty, self.target)) {
3008 return result;3015 return result;
3009 }3016 }
3010 return try self.load(result, elem_ty, 0);3017 return self.load(result, elem_ty, 0);
3011}3018}
30123019
3013fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3020fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -3023,7 +3030,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3023,7 +3030,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30233030
3024 // load pointer onto the stack3031 // load pointer onto the stack
3025 if (ptr_ty.isSlice()) {3032 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);
3027 try self.addLabel(.local_get, ptr_local.local);3034 try self.addLabel(.local_get, ptr_local.local);
3028 } else {3035 } else {
3029 try self.emitWValue(ptr);3036 try self.emitWValue(ptr);
...@@ -3157,7 +3164,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3157,7 +3164,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3157 if (isByRef(elem_ty, self.target)) {3164 if (isByRef(elem_ty, self.target)) {
3158 return result;3165 return result;
3159 }3166 }
3160 return try self.load(result, elem_ty, 0);3167 return self.load(result, elem_ty, 0);
3161}3168}
31623169
3163fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3170fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -3201,8 +3208,63 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3201,8 +3208,63 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3201 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3208 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3202 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);3209 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
32033210
3204 _ = elements;3211 switch (vector_ty.zigTypeTag()) {
3205 return self.fail("TODO: Wasm backend: implement airVectorInit", .{});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 }
3206}3268}
32073269
3208fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3270fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {