authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-07-16 22:43:06+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 12:19:16-07:00
log2438f61f1c37aefa16852130370df44b3fabf785
tree831f7d890c87cf50119f5f639a2818e9a7b1c8ba
parent424f260f850cb22637888bbfdf5bfaf9c08a4dae

Refactor entire wasm-backend to use new AIR memory layout


2 files changed, 161 insertions(+), 116 deletions(-)

src/codegen/wasm.zig+160-115
...@@ -483,8 +483,8 @@ pub const Result = union(enum) {...@@ -483,8 +483,8 @@ pub const Result = union(enum) {
483 externally_managed: []const u8,483 externally_managed: []const u8,
484};484};
485485
486/// Hashmap to store generated `WValue` for each `Inst`486/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
487pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Index, WValue);487pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Ref, WValue);
488488
489/// Code represents the `Code` section of wasm that489/// Code represents the `Code` section of wasm that
490/// belongs to a function490/// belongs to a function
...@@ -495,7 +495,7 @@ pub const Context = struct {...@@ -495,7 +495,7 @@ pub const Context = struct {
495 air: Air,495 air: Air,
496 liveness: Liveness,496 liveness: Liveness,
497 gpa: *mem.Allocator,497 gpa: *mem.Allocator,
498 /// Table to save `WValue`'s generated by an `Inst`498 /// Table to save `WValue`'s generated by an `Air.Inst`
499 values: ValueTable,499 values: ValueTable,
500 /// Mapping from Air.Inst.Index to block ids500 /// Mapping from Air.Inst.Index to block ids
501 blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},501 blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},
...@@ -547,14 +547,15 @@ pub const Context = struct {...@@ -547,14 +547,15 @@ pub const Context = struct {
547547
548 /// Resolves the `WValue` for the given instruction `inst`548 /// Resolves the `WValue` for the given instruction `inst`
549 /// When the given instruction has a `Value`, it returns a constant instead549 /// When the given instruction has a `Value`, it returns a constant instead
550 fn resolveInst(self: Context, inst: Air.Inst) Index {550 fn resolveInst(self: Context, ref: Air.Inst.Ref) WValue {
551 if (!inst.ty.hasCodeGenBits()) return .none;551 const ref_type = self.air.getRefType(ref);
552 if (ref_type.hasCodeGenBits()) return .none;
552553
553 if (inst.value()) |_| {554 if (self.air.instructions.items(.tag)[@enumToInt(ref)] == .constant) {
554 return WValue{ .constant = inst };555 return WValue{ .constant = @enumToInt(ref) };
555 }556 }
556557
557 return self.values.get(inst).?; // Instruction does not dominate all uses!558 return self.values.get(ref).?; // Instruction does not dominate all uses!
558 }559 }
559560
560 /// Using a given `Type`, returns the corresponding wasm Valtype561 /// Using a given `Type`, returns the corresponding wasm Valtype
...@@ -610,7 +611,12 @@ pub const Context = struct {...@@ -610,7 +611,12 @@ pub const Context = struct {
610 try writer.writeByte(wasm.opcode(.local_get));611 try writer.writeByte(wasm.opcode(.local_get));
611 try leb.writeULEB128(writer, idx);612 try leb.writeULEB128(writer, idx);
612 },613 },
613 .constant => |inst| try self.emitConstant(inst.value().?, inst.ty), // creates a new constant onto the stack614 .constant => |index| {
615 const ty_pl = self.air.instructions.items(.data)[index].ty_pl;
616 const value = self.air.values[ty_pl.payload];
617 // create a new constant onto the stack
618 try self.emitConstant(value, self.air.getRefType(ty_pl.ty));
619 },
614 }620 }
615 }621 }
616622
...@@ -626,10 +632,7 @@ pub const Context = struct {...@@ -626,10 +632,7 @@ pub const Context = struct {
626 const fields_len = @intCast(u32, struct_data.fields.count());632 const fields_len = @intCast(u32, struct_data.fields.count());
627 try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len);633 try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len);
628 for (struct_data.fields.values()) |*value| {634 for (struct_data.fields.values()) |*value| {
629 const val_type = try self.genValtype(635 const val_type = try self.genValtype(value.ty);
630 .{ .node_offset = struct_data.node_offset },
631 value.ty,
632 );
633 self.locals.appendAssumeCapacity(val_type);636 self.locals.appendAssumeCapacity(val_type);
634 self.local_index += 1;637 self.local_index += 1;
635 }638 }
...@@ -640,7 +643,7 @@ pub const Context = struct {...@@ -640,7 +643,7 @@ pub const Context = struct {
640 },643 },
641 .ErrorUnion => {644 .ErrorUnion => {
642 const payload_type = ty.errorUnionChild();645 const payload_type = ty.errorUnionChild();
643 const val_type = try self.genValtype(.{ .node_offset = 0 }, payload_type);646 const val_type = try self.genValtype(payload_type);
644647
645 // we emit the error value as the first local, and the payload as the following.648 // we emit the error value as the first local, and the payload as the following.
646 // The first local is also used to find the index of the error and payload.649 // The first local is also used to find the index of the error and payload.
...@@ -657,7 +660,7 @@ pub const Context = struct {...@@ -657,7 +660,7 @@ pub const Context = struct {
657 } };660 } };
658 },661 },
659 else => {662 else => {
660 const valtype = try self.genValtype(.{ .node_offset = 0 }, ty);663 const valtype = try self.genValtype(ty);
661 try self.locals.append(self.gpa, valtype);664 try self.locals.append(self.gpa, valtype);
662 self.local_index += 1;665 self.local_index += 1;
663 return WValue{ .local = initial_index };666 return WValue{ .local = initial_index };
...@@ -708,8 +711,7 @@ pub const Context = struct {...@@ -708,8 +711,7 @@ pub const Context = struct {
708 }711 }
709 }712 }
710713
711 pub fn genFunc(self: *Context, func: *Module.Fn) InnerError!Result {714 pub fn genFunc(self: *Context) InnerError!Result {
712 _ = func;
713 try self.genFunctype();715 try self.genFunctype();
714 // TODO: check for and handle death of instructions716 // TODO: check for and handle death of instructions
715717
...@@ -790,44 +792,43 @@ pub const Context = struct {...@@ -790,44 +792,43 @@ pub const Context = struct {
790 fn genInst(self: *Context, inst: Air.Inst.Index) !WValue {792 fn genInst(self: *Context, inst: Air.Inst.Index) !WValue {
791 const air_tags = self.air.instructions.items(.tag);793 const air_tags = self.air.instructions.items(.tag);
792 return switch (air_tags[inst]) {794 return switch (air_tags[inst]) {
793 // .add => self.genBinOp(inst.castTag(.add).?, .add),795 .add => self.genBinOp(inst, .add),
794 // .alloc => self.genAlloc(inst.castTag(.alloc).?),796 .alloc => self.genAlloc(inst),
795 // .arg => self.genArg(inst.castTag(.arg).?),797 .arg => self.genArg(inst),
796 // .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"),798 .bit_and => self.genBinOp(inst, .@"and"),
797 // .bitcast => self.genBitcast(inst.castTag(.bitcast).?),799 .bitcast => self.genBitcast(inst),
798 // .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"),800 .bit_or => self.genBinOp(inst, .@"or"),
799 // .block => self.genBlock(inst.castTag(.block).?),801 .block => self.genBlock(inst),
800 // .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"),802 .bool_and => self.genBinOp(inst, .@"and"),
801 // .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"),803 .bool_or => self.genBinOp(inst, .@"or"),
802 // .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),804 .breakpoint => self.genBreakpoint(inst),
803 // .br => self.genBr(inst.castTag(.br).?),805 .br => self.genBr(inst),
804 // .call => self.genCall(inst.castTag(.call).?),806 .call => self.genCall(inst),
805 // .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),807 .cmp_eq => self.genCmp(inst, .eq),
806 // .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),808 .cmp_gte => self.genCmp(inst, .gte),
807 // .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),809 .cmp_gt => self.genCmp(inst, .gt),
808 // .cmp_lte => self.genCmp(inst.castTag(.cmp_lte).?, .lte),810 .cmp_lte => self.genCmp(inst, .lte),
809 // .cmp_lt => self.genCmp(inst.castTag(.cmp_lt).?, .lt),811 .cmp_lt => self.genCmp(inst, .lt),
810 // .cmp_neq => self.genCmp(inst.castTag(.cmp_neq).?, .neq),812 .cmp_neq => self.genCmp(inst, .neq),
811 // .condbr => self.genCondBr(inst.castTag(.condbr).?),813 .cond_br => self.genCondBr(inst),
812 // .constant => unreachable,814 .constant => unreachable,
813 // .dbg_stmt => WValue.none,815 .dbg_stmt => WValue.none,
814 // .div => self.genBinOp(inst.castTag(.div).?, .div),816 .div => self.genBinOp(inst, .div),
815 // .is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne),817 .is_err => self.genIsErr(inst, .i32_ne),
816 // .is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq),818 .is_non_err => self.genIsErr(inst, .i32_eq),
817 // .load => self.genLoad(inst.castTag(.load).?),819 .load => self.genLoad(inst),
818 // .loop => self.genLoop(inst.castTag(.loop).?),820 .loop => self.genLoop(inst),
819 // .mul => self.genBinOp(inst.castTag(.mul).?, .mul),821 .mul => self.genBinOp(inst, .mul),
820 // .not => self.genNot(inst.castTag(.not).?),822 .not => self.genNot(inst),
821 // .ret => self.genRet(inst.castTag(.ret).?),823 .ret => self.genRet(inst),
822 // .retvoid => WValue.none,824 .store => self.genStore(inst),
823 // .store => self.genStore(inst.castTag(.store).?),825 .struct_field_ptr => self.genStructFieldPtr(inst),
824 // .struct_field_ptr => self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),826 .sub => self.genBinOp(inst, .sub),
825 // .sub => self.genBinOp(inst.castTag(.sub).?, .sub),827 .switch_br => self.genSwitchBr(inst),
826 // .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?),828 .unreach => self.genUnreachable(inst),
827 // .unreach => self.genUnreachable(inst.castTag(.unreach).?),829 .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst),
828 // .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?),830 .wrap_errunion_payload => self.genWrapErrUnionPayload(inst),
829 // .wrap_errunion_payload => self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),831 .xor => self.genBinOp(inst, .xor),
830 // .xor => self.genBinOp(inst.castTag(.xor).?, .xor),
831 else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),832 else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
832 };833 };
833 }834 }
...@@ -835,22 +836,27 @@ pub const Context = struct {...@@ -835,22 +836,27 @@ pub const Context = struct {
835 fn genBody(self: *Context, body: []const Air.Inst.Index) InnerError!void {836 fn genBody(self: *Context, body: []const Air.Inst.Index) InnerError!void {
836 for (body) |inst| {837 for (body) |inst| {
837 const result = try self.genInst(inst);838 const result = try self.genInst(inst);
838 try self.values.putNoClobber(self.gpa, inst, result);839 try self.values.putNoClobber(self.gpa, @intToEnum(Air.Inst.Ref, inst), result);
839 }840 }
840 }841 }
841842
842 fn genRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue {843 fn genRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
843 // TODO: Implement tail calls844 const un_op = self.air.instructions.items(.data)[inst].un_op;
844 const operand = self.resolveInst(inst.operand);845 const operand = self.resolveInst(un_op);
845 try self.emitWValue(operand);846 try self.emitWValue(operand);
846 try self.code.append(wasm.opcode(.@"return"));847 try self.code.append(wasm.opcode(.@"return"));
847 return .none;848 return .none;
848 }849 }
849850
850 fn genCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue {851 fn genCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
851 const func_val = inst.func.value().?;852 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
853 const extra = self.air.extraData(Air.Call, pl_op.payload);
854 const args = self.air.extra[extra.end..][0..extra.data.args_len];
852855
853 const target: *Decl = blk: {856 const target: *Decl = blk: {
857 const ty_pl = self.air.instructions.items(.data)[@enumToInt(pl_op.operand)].ty_pl;
858 const func_val = self.air.values[ty_pl.payload];
859
854 if (func_val.castTag(.function)) |func| {860 if (func_val.castTag(.function)) |func| {
855 break :blk func.data.owner_decl;861 break :blk func.data.owner_decl;
856 } else if (func_val.castTag(.extern_fn)) |ext_fn| {862 } else if (func_val.castTag(.extern_fn)) |ext_fn| {
...@@ -859,8 +865,8 @@ pub const Context = struct {...@@ -859,8 +865,8 @@ pub const Context = struct {
859 return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()});865 return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()});
860 };866 };
861867
862 for (inst.args) |arg| {868 for (args) |arg| {
863 const arg_val = self.resolveInst(arg);869 const arg_val = self.resolveInst(@intToEnum(Air.Inst.Ref, arg));
864 try self.emitWValue(arg_val);870 try self.emitWValue(arg_val);
865 }871 }
866872
...@@ -877,15 +883,16 @@ pub const Context = struct {...@@ -877,15 +883,16 @@ pub const Context = struct {
877 }883 }
878884
879 fn genAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue {885 fn genAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
880 const elem_type = inst.base.ty.elemType();886 const elem_type = self.air.getType(inst).elemType();
881 return self.allocLocal(elem_type);887 return self.allocLocal(elem_type);
882 }888 }
883889
884 fn genStore(self: *Context, inst: Air.Inst.Index) InnerError!WValue {890 fn genStore(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
891 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
885 const writer = self.code.writer();892 const writer = self.code.writer();
886893
887 const lhs = self.resolveInst(inst.lhs);894 const lhs = self.resolveInst(bin_op.lhs);
888 const rhs = self.resolveInst(inst.rhs);895 const rhs = self.resolveInst(bin_op.rhs);
889896
890 switch (lhs) {897 switch (lhs) {
891 .multi_value => |multi_value| switch (rhs) {898 .multi_value => |multi_value| switch (rhs) {
...@@ -893,7 +900,7 @@ pub const Context = struct {...@@ -893,7 +900,7 @@ pub const Context = struct {
893 // we simply assign the local_index to the rhs one.900 // we simply assign the local_index to the rhs one.
894 // This allows us to update struct fields without having to individually901 // This allows us to update struct fields without having to individually
895 // set each local as each field's index will be calculated off the struct's base index902 // set each local as each field's index will be calculated off the struct's base index
896 .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses!903 .multi_value => self.values.put(self.gpa, bin_op.lhs, rhs) catch unreachable, // Instruction does not dominate all uses!
897 .constant, .none => {904 .constant, .none => {
898 // emit all values onto the stack if constant905 // emit all values onto the stack if constant
899 try self.emitWValue(rhs);906 try self.emitWValue(rhs);
...@@ -920,7 +927,8 @@ pub const Context = struct {...@@ -920,7 +927,8 @@ pub const Context = struct {
920 }927 }
921928
922 fn genLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue {929 fn genLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
923 return self.resolveInst(inst.operand);930 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
931 return self.resolveInst(ty_op.operand);
924 }932 }
925933
926 fn genArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue {934 fn genArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
...@@ -931,8 +939,9 @@ pub const Context = struct {...@@ -931,8 +939,9 @@ pub const Context = struct {
931 }939 }
932940
933 fn genBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue {941 fn genBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue {
934 const lhs = self.resolveInst(inst.lhs);942 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
935 const rhs = self.resolveInst(inst.rhs);943 const lhs = self.resolveInst(bin_op.lhs);
944 const rhs = self.resolveInst(bin_op.rhs);
936945
937 // it's possible for both lhs and/or rhs to return an offset as well,946 // it's possible for both lhs and/or rhs to return an offset as well,
938 // in which case we return the first offset occurance we find.947 // in which case we return the first offset occurance we find.
...@@ -945,10 +954,11 @@ pub const Context = struct {...@@ -945,10 +954,11 @@ pub const Context = struct {
945 try self.emitWValue(lhs);954 try self.emitWValue(lhs);
946 try self.emitWValue(rhs);955 try self.emitWValue(rhs);
947956
957 const bin_ty = self.air.getRefType(bin_op.lhs);
948 const opcode: wasm.Opcode = buildOpcode(.{958 const opcode: wasm.Opcode = buildOpcode(.{
949 .op = op,959 .op = op,
950 .valtype1 = try self.typeToValtype(inst.base.ty),960 .valtype1 = try self.typeToValtype(bin_ty),
951 .signedness = if (inst.base.ty.isSignedInt()) .signed else .unsigned,961 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
952 });962 });
953 try self.code.append(wasm.opcode(opcode));963 try self.code.append(wasm.opcode(opcode));
954 return WValue{ .code_offset = offset };964 return WValue{ .code_offset = offset };
...@@ -1064,14 +1074,17 @@ pub const Context = struct {...@@ -1064,14 +1074,17 @@ pub const Context = struct {
1064 }1074 }
1065 }1075 }
10661076
1067 fn genBlock(self: *Context, block: Air.Inst.Index) InnerError!WValue {1077 fn genBlock(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1068 const block_ty = try self.genBlockType(block.base.ty);1078 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1079 const block_ty = try self.genBlockType(self.air.getRefType(ty_pl.ty));
1080 const extra = self.air.extraData(Air.Block, ty_pl.payload);
1081 const body = self.air.extra[extra.end..][0..extra.data.body_len];
10691082
1070 try self.startBlock(.block, block_ty, null);1083 try self.startBlock(.block, block_ty, null);
1071 // Here we set the current block idx, so breaks know the depth to jump1084 // Here we set the current block idx, so breaks know the depth to jump
1072 // to when breaking out.1085 // to when breaking out.
1073 try self.blocks.putNoClobber(self.gpa, block, self.block_depth);1086 try self.blocks.putNoClobber(self.gpa, inst, self.block_depth);
1074 try self.genBody(block.body);1087 try self.genBody(body);
1075 try self.endBlock();1088 try self.endBlock();
10761089
1077 return .none;1090 return .none;
...@@ -1095,11 +1108,15 @@ pub const Context = struct {...@@ -1095,11 +1108,15 @@ pub const Context = struct {
1095 self.block_depth -= 1;1108 self.block_depth -= 1;
1096 }1109 }
10971110
1098 fn genLoop(self: *Context, loop: Air.Inst.Index) InnerError!WValue {1111 fn genLoop(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1099 const loop_ty = try self.genBlockType(loop.base.ty);1112 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1113 const loop = self.air.extraData(Air.Block, ty_pl.payload);
1114 const body = self.air.extra[loop.end..][0..loop.data.body_len];
11001115
1101 try self.startBlock(.loop, loop_ty, null);1116 // result type of loop is always 'noreturn', meaning we can always
1102 try self.genBody(loop.body);1117 // emit the wasm type 'block_empty'.
1118 try self.startBlock(.loop, wasm.block_empty, null);
1119 try self.genBody(body);
11031120
1104 // breaking to the index of a loop block will continue the loop instead1121 // breaking to the index of a loop block will continue the loop instead
1105 try self.code.append(wasm.opcode(.br));1122 try self.code.append(wasm.opcode(.br));
...@@ -1110,8 +1127,12 @@ pub const Context = struct {...@@ -1110,8 +1127,12 @@ pub const Context = struct {
1110 return .none;1127 return .none;
1111 }1128 }
11121129
1113 fn genCondBr(self: *Context, condbr: Air.Inst.Index) InnerError!WValue {1130 fn genCondBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1114 const condition = self.resolveInst(condbr.condition);1131 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1132 const condition = self.resolveInst(pl_op.operand);
1133 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
1134 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
1135 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1115 const writer = self.code.writer();1136 const writer = self.code.writer();
11161137
1117 // TODO: Handle death instructions for then and else body1138 // TODO: Handle death instructions for then and else body
...@@ -1126,8 +1147,9 @@ pub const Context = struct {...@@ -1126,8 +1147,9 @@ pub const Context = struct {
1126 break :blk offset;1147 break :blk offset;
1127 },1148 },
1128 };1149 };
1129 const block_ty = try self.genBlockType(condbr.base.ty);1150
1130 try self.startBlock(.block, block_ty, offset);1151 // result type is always noreturn, so use `block_empty` as type.
1152 try self.startBlock(.block, wasm.block_empty, offset);
11311153
1132 // we inserted the block in front of the condition1154 // we inserted the block in front of the condition
1133 // so now check if condition matches. If not, break outside this block1155 // so now check if condition matches. If not, break outside this block
...@@ -1135,11 +1157,11 @@ pub const Context = struct {...@@ -1135,11 +1157,11 @@ pub const Context = struct {
1135 try writer.writeByte(wasm.opcode(.br_if));1157 try writer.writeByte(wasm.opcode(.br_if));
1136 try leb.writeULEB128(writer, @as(u32, 0));1158 try leb.writeULEB128(writer, @as(u32, 0));
11371159
1138 try self.genBody(condbr.else_body);1160 try self.genBody(else_body);
1139 try self.endBlock();1161 try self.endBlock();
11401162
1141 // Outer block that matches the condition1163 // Outer block that matches the condition
1142 try self.genBody(condbr.then_body);1164 try self.genBody(then_body);
11431165
1144 return .none;1166 return .none;
1145 }1167 }
...@@ -1149,21 +1171,23 @@ pub const Context = struct {...@@ -1149,21 +1171,23 @@ pub const Context = struct {
1149 // the comparison that we can later jump back to1171 // the comparison that we can later jump back to
1150 const offset = self.code.items.len;1172 const offset = self.code.items.len;
11511173
1152 const lhs = self.resolveInst(inst.lhs);1174 const data: Air.Inst.Data = self.air.instructions.items(.data)[inst];
1153 const rhs = self.resolveInst(inst.rhs);1175 const lhs = self.resolveInst(data.bin_op.lhs);
1176 const rhs = self.resolveInst(data.bin_op.rhs);
1177 const lhs_ty = self.air.getRefType(data.bin_op.lhs);
11541178
1155 try self.emitWValue(lhs);1179 try self.emitWValue(lhs);
1156 try self.emitWValue(rhs);1180 try self.emitWValue(rhs);
11571181
1158 const signedness: std.builtin.Signedness = blk: {1182 const signedness: std.builtin.Signedness = blk: {
1159 // by default we tell the operand type is unsigned (i.e. bools and enum values)1183 // by default we tell the operand type is unsigned (i.e. bools and enum values)
1160 if (inst.lhs.ty.zigTypeTag() != .Int) break :blk .unsigned;1184 if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned;
11611185
1162 // incase of an actual integer, we emit the correct signedness1186 // incase of an actual integer, we emit the correct signedness
1163 break :blk inst.lhs.ty.intInfo(self.target).signedness;1187 break :blk lhs_ty.intInfo(self.target).signedness;
1164 };1188 };
1165 const opcode: wasm.Opcode = buildOpcode(.{1189 const opcode: wasm.Opcode = buildOpcode(.{
1166 .valtype1 = try self.typeToValtype(inst.lhs.ty),1190 .valtype1 = try self.typeToValtype(lhs_ty),
1167 .op = switch (op) {1191 .op = switch (op) {
1168 .lt => .lt,1192 .lt => .lt,
1169 .lte => .le,1193 .lte => .le,
...@@ -1178,16 +1202,17 @@ pub const Context = struct {...@@ -1178,16 +1202,17 @@ pub const Context = struct {
1178 return WValue{ .code_offset = offset };1202 return WValue{ .code_offset = offset };
1179 }1203 }
11801204
1181 fn genBr(self: *Context, br: Air.Inst.Index) InnerError!WValue {1205 fn genBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1206 const br = self.air.instructions.items(.data)[inst].br;
1207
1182 // if operand has codegen bits we should break with a value1208 // if operand has codegen bits we should break with a value
1183 if (br.operand.ty.hasCodeGenBits()) {1209 if (self.air.getRefType(br.operand).hasCodeGenBits()) {
1184 const operand = self.resolveInst(br.operand);1210 try self.emitWValue(self.resolveInst(br.operand));
1185 try self.emitWValue(operand);
1186 }1211 }
11871212
1188 // We map every block to its block index.1213 // We map every block to its block index.
1189 // We then determine how far we have to jump to it by substracting it from current block depth1214 // We then determine how far we have to jump to it by substracting it from current block depth
1190 const idx: u32 = self.block_depth - self.blocks.get(br.block).?;1215 const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;
1191 const writer = self.code.writer();1216 const writer = self.code.writer();
1192 try writer.writeByte(wasm.opcode(.br));1217 try writer.writeByte(wasm.opcode(.br));
1193 try leb.writeULEB128(writer, idx);1218 try leb.writeULEB128(writer, idx);
...@@ -1195,10 +1220,11 @@ pub const Context = struct {...@@ -1195,10 +1220,11 @@ pub const Context = struct {
1195 return .none;1220 return .none;
1196 }1221 }
11971222
1198 fn genNot(self: *Context, not: Air.Inst.Index) InnerError!WValue {1223 fn genNot(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1224 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1199 const offset = self.code.items.len;1225 const offset = self.code.items.len;
12001226
1201 const operand = self.resolveInst(not.operand);1227 const operand = self.resolveInst(ty_op.operand);
1202 try self.emitWValue(operand);1228 try self.emitWValue(operand);
12031229
1204 // wasm does not have booleans nor the `not` instruction, therefore compare with 01230 // wasm does not have booleans nor the `not` instruction, therefore compare with 0
...@@ -1212,35 +1238,44 @@ pub const Context = struct {...@@ -1212,35 +1238,44 @@ pub const Context = struct {
1212 return WValue{ .code_offset = offset };1238 return WValue{ .code_offset = offset };
1213 }1239 }
12141240
1215 fn genBreakpoint(self: *Context, breakpoint: Air.Inst.Index) InnerError!WValue {1241 fn genBreakpoint(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1216 _ = self;1242 _ = self;
1217 _ = breakpoint;1243 _ = inst;
1218 // unsupported by wasm itself. Can be implemented once we support DWARF1244 // unsupported by wasm itself. Can be implemented once we support DWARF
1219 // for wasm1245 // for wasm
1220 return .none;1246 return .none;
1221 }1247 }
12221248
1223 fn genUnreachable(self: *Context, unreach: Air.Inst.Index) InnerError!WValue {1249 fn genUnreachable(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1224 _ = unreach;1250 _ = inst;
1225 try self.code.append(wasm.opcode(.@"unreachable"));1251 try self.code.append(wasm.opcode(.@"unreachable"));
1226 return .none;1252 return .none;
1227 }1253 }
12281254
1229 fn genBitcast(self: *Context, bitcast: Air.Inst.Index) InnerError!WValue {1255 fn genBitcast(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1230 return self.resolveInst(bitcast.operand);1256 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1257 return self.resolveInst(ty_op.operand);
1231 }1258 }
12321259
1233 fn genStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {1260 fn genStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1234 const struct_ptr = self.resolveInst(inst.struct_ptr);1261 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1262 const extra = self.air.extraData(Air.StructField, ty_pl.payload);
1263 const struct_ptr = self.resolveInst(extra.data.struct_ptr);
12351264
1236 return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, inst.field_index) };1265 return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, extra.data.field_index) };
1237 }1266 }
12381267
1239 fn genSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {1268 fn genSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1240 const target = self.resolveInst(inst.target);1269 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1241 const target_ty = inst.target.ty;1270 const extra = self.air.extraData(Air.SwitchBr, pl_op.payload);
1242 const valtype = try self.typeToValtype(.{ .node_offset = 0 }, target_ty);1271 const cases = self.air.extra[extra.end..][0..extra.data.cases_len];
1243 const blocktype = try self.genBlockType(inst.base.ty);1272 const else_body = self.air.extra[extra.end + cases.len ..][0..extra.data.else_body_len];
1273
1274 const target = self.resolveInst(pl_op.operand);
1275 const target_ty = self.air.getRefType(pl_op.operand);
1276 const valtype = try self.typeToValtype(target_ty);
1277 // result type is always 'noreturn'
1278 const blocktype = wasm.block_empty;
12441279
1245 const signedness: std.builtin.Signedness = blk: {1280 const signedness: std.builtin.Signedness = blk: {
1246 // by default we tell the operand type is unsigned (i.e. bools and enum values)1281 // by default we tell the operand type is unsigned (i.e. bools and enum values)
...@@ -1249,11 +1284,18 @@ pub const Context = struct {...@@ -1249,11 +1284,18 @@ pub const Context = struct {
1249 // incase of an actual integer, we emit the correct signedness1284 // incase of an actual integer, we emit the correct signedness
1250 break :blk target_ty.intInfo(self.target).signedness;1285 break :blk target_ty.intInfo(self.target).signedness;
1251 };1286 };
1252 for (inst.cases) |case| {1287 for (cases) |case_idx| {
1288 const case = self.air.extraData(Air.SwitchBr.Case, case_idx);
1289 const case_body = self.air.extra[case.end..][0..case.data.body_len];
1290
1253 // create a block for each case, when the condition does not match we break out of it1291 // create a block for each case, when the condition does not match we break out of it
1254 try self.startBlock(.block, blocktype, null);1292 try self.startBlock(.block, blocktype, null);
1255 try self.emitWValue(target);1293 try self.emitWValue(target);
1256 try self.emitConstant(.{ .node_offset = 0 }, case.item, target_ty);1294
1295 // cases must represent a constant of which its type is in the `typed_value_map`
1296 // Therefore we can simply retrieve it.
1297 const ty_val = Air.Inst.Ref.typed_value_map[@enumToInt(case.data.item)];
1298 try self.emitConstant(ty_val.val, target_ty);
1257 const opcode = buildOpcode(.{1299 const opcode = buildOpcode(.{
1258 .valtype1 = valtype,1300 .valtype1 = valtype,
1259 .op = .ne, // not equal because we jump out the block if it does not match the condition1301 .op = .ne, // not equal because we jump out the block if it does not match the condition
...@@ -1264,7 +1306,7 @@ pub const Context = struct {...@@ -1264,7 +1306,7 @@ pub const Context = struct {
1264 try leb.writeULEB128(self.code.writer(), @as(u32, 0));1306 try leb.writeULEB128(self.code.writer(), @as(u32, 0));
12651307
1266 // emit our block code1308 // emit our block code
1267 try self.genBody(case.body);1309 try self.genBody(case_body);
12681310
1269 // end the block we created earlier1311 // end the block we created earlier
1270 try self.endBlock();1312 try self.endBlock();
...@@ -1272,13 +1314,14 @@ pub const Context = struct {...@@ -1272,13 +1314,14 @@ pub const Context = struct {
12721314
1273 // finally, emit the else case if it exists. Here we will not have to1315 // finally, emit the else case if it exists. Here we will not have to
1274 // check for a condition, so also no need to emit a block.1316 // check for a condition, so also no need to emit a block.
1275 try self.genBody(inst.else_body);1317 try self.genBody(else_body);
12761318
1277 return .none;1319 return .none;
1278 }1320 }
12791321
1280 fn genIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {1322 fn genIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
1281 const operand = self.resolveInst(inst.operand);1323 const un_op = self.air.instructions.items(.data)[inst].un_op;
1324 const operand = self.resolveInst(un_op);
1282 const offset = self.code.items.len;1325 const offset = self.code.items.len;
1283 const writer = self.code.writer();1326 const writer = self.code.writer();
12841327
...@@ -1294,7 +1337,8 @@ pub const Context = struct {...@@ -1294,7 +1337,8 @@ pub const Context = struct {
1294 }1337 }
12951338
1296 fn genUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {1339 fn genUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1297 const operand = self.resolveInst(inst.operand);1340 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1341 const operand = self.resolveInst(ty_op.operand);
1298 // The index of multi_value contains the error code. To get the initial index of the payload we get1342 // The index of multi_value contains the error code. To get the initial index of the payload we get
1299 // the following index. Next, convert it to a `WValue.local`1343 // the following index. Next, convert it to a `WValue.local`
1300 //1344 //
...@@ -1303,6 +1347,7 @@ pub const Context = struct {...@@ -1303,6 +1347,7 @@ pub const Context = struct {
1303 }1347 }
13041348
1305 fn genWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {1349 fn genWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1306 return self.resolveInst(inst.operand);1350 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1351 return self.resolveInst(ty_op.operand);
1307 }1352 }
1308};1353};
src/link/Wasm.zig+1-1
...@@ -220,7 +220,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live...@@ -220,7 +220,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
220 defer context.deinit();220 defer context.deinit();
221221
222 // generate the 'code' section for the function declaration222 // generate the 'code' section for the function declaration
223 const result = context.genFunc(func) catch |err| switch (err) {223 const result = context.genFunc() catch |err| switch (err) {
224 error.CodegenFail => {224 error.CodegenFail => {
225 decl.analysis = .codegen_failure;225 decl.analysis = .codegen_failure;
226 try module.failed_decls.put(module.gpa, decl, context.err_msg);226 try module.failed_decls.put(module.gpa, decl, context.err_msg);