authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-05-15 16:52:01+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-05-19 08:59:08+02:00
logb22e22ef55327055e88817ae2d950da4739e488d
treeb9ea437dfe6fb64eb07d09c2e64084ecf27cb8f1
parent1d6c804b29fed146aafda0f56cae7a4853b6abf9
signaturelock-open Commit is signed but in an unrecognized format.

wasm backend - Initial enum support

- This adds support for enum values using field indexes - EmitConstant's signature was changed so it's easier to recursively call it using a different type (enum -> int type). - Implemented initial support for bitcast which for now just returns the `WValue` of the operand.

1 files changed, 66 insertions(+), 25 deletions(-)

src/codegen/wasm.zig+66-25
......@@ -557,7 +557,14 @@ pub const Context = struct {
557557 return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits});
558558 },
559559 .Bool, .Pointer => wasm.Valtype.i32,
560 else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.tag()}),
560 .Enum => switch (ty.tag()) {
561 .enum_simple => wasm.Valtype.i32,
562 else => self.typeToValtype(
563 src,
564 ty.cast(Type.Payload.EnumFull).?.data.tag_ty,
565 ),
566 },
567 else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}),
561568 };
562569 }
563570
......@@ -586,7 +593,7 @@ pub const Context = struct {
586593 try writer.writeByte(wasm.opcode(.local_get));
587594 try leb.writeULEB128(writer, idx);
588595 },
589 .constant => |inst| try self.emitConstant(inst.castTag(.constant).?), // creates a new constant onto the stack
596 .constant => |inst| try self.emitConstant(inst.src, inst.value().?, inst.ty), // creates a new constant onto the stack
590597 }
591598 }
592599
......@@ -707,14 +714,15 @@ pub const Context = struct {
707714 .add => self.genBinOp(inst.castTag(.add).?, .add),
708715 .alloc => self.genAlloc(inst.castTag(.alloc).?),
709716 .arg => self.genArg(inst.castTag(.arg).?),
717 .bitcast => self.genBitcast(inst.castTag(.bitcast).?),
718 .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"),
719 .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"),
710720 .block => self.genBlock(inst.castTag(.block).?),
721 .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"),
722 .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"),
711723 .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
712724 .br => self.genBr(inst.castTag(.br).?),
713725 .call => self.genCall(inst.castTag(.call).?),
714 .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"),
715 .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"),
716 .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"),
717 .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"),
718726 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
719727 .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),
720728 .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),
......@@ -724,18 +732,18 @@ pub const Context = struct {
724732 .condbr => self.genCondBr(inst.castTag(.condbr).?),
725733 .constant => unreachable,
726734 .dbg_stmt => WValue.none,
735 .div => self.genBinOp(inst.castTag(.div).?, .div),
727736 .load => self.genLoad(inst.castTag(.load).?),
728737 .loop => self.genLoop(inst.castTag(.loop).?),
729738 .mul => self.genBinOp(inst.castTag(.mul).?, .mul),
730 .div => self.genBinOp(inst.castTag(.div).?, .div),
731 .xor => self.genBinOp(inst.castTag(.xor).?, .xor),
732739 .not => self.genNot(inst.castTag(.not).?),
733740 .ret => self.genRet(inst.castTag(.ret).?),
734741 .retvoid => WValue.none,
735742 .store => self.genStore(inst.castTag(.store).?),
736743 .sub => self.genBinOp(inst.castTag(.sub).?, .sub),
737744 .unreach => self.genUnreachable(inst.castTag(.unreach).?),
738 else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),
745 .xor => self.genBinOp(inst.castTag(.xor).?, .xor),
746 else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}),
739747 };
740748 }
741749
......@@ -830,44 +838,44 @@ pub const Context = struct {
830838 return .none;
831839 }
832840
833 fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void {
841 fn emitConstant(self: *Context, src: LazySrcLoc, value: Value, ty: Type) InnerError!void {
834842 const writer = self.code.writer();
835 switch (inst.base.ty.zigTypeTag()) {
843 switch (ty.zigTypeTag()) {
836844 .Int => {
837845 // write opcode
838846 const opcode: wasm.Opcode = buildOpcode(.{
839847 .op = .@"const",
840 .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty),
848 .valtype1 = try self.typeToValtype(src, ty),
841849 });
842850 try writer.writeByte(wasm.opcode(opcode));
843851 // write constant
844 switch (inst.base.ty.intInfo(self.target).signedness) {
845 .signed => try leb.writeILEB128(writer, inst.val.toSignedInt()),
846 .unsigned => try leb.writeILEB128(writer, inst.val.toUnsignedInt()),
852 switch (ty.intInfo(self.target).signedness) {
853 .signed => try leb.writeILEB128(writer, value.toSignedInt()),
854 .unsigned => try leb.writeILEB128(writer, value.toUnsignedInt()),
847855 }
848856 },
849857 .Bool => {
850858 // write opcode
851859 try writer.writeByte(wasm.opcode(.i32_const));
852860 // write constant
853 try leb.writeILEB128(writer, inst.val.toSignedInt());
861 try leb.writeILEB128(writer, value.toSignedInt());
854862 },
855863 .Float => {
856864 // write opcode
857865 const opcode: wasm.Opcode = buildOpcode(.{
858866 .op = .@"const",
859 .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty),
867 .valtype1 = try self.typeToValtype(src, ty),
860868 });
861869 try writer.writeByte(wasm.opcode(opcode));
862870 // write constant
863 switch (inst.base.ty.floatBits(self.target)) {
864 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, inst.val.toFloat(f32))),
865 64 => try writer.writeIntLittle(u64, @bitCast(u64, inst.val.toFloat(f64))),
866 else => |bits| return self.fail(inst.base.src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}),
871 switch (ty.floatBits(self.target)) {
872 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, value.toFloat(f32))),
873 64 => try writer.writeIntLittle(u64, @bitCast(u64, value.toFloat(f64))),
874 else => |bits| return self.fail(src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}),
867875 }
868876 },
869877 .Pointer => {
870 if (inst.val.castTag(.decl_ref)) |payload| {
878 if (value.castTag(.decl_ref)) |payload| {
871879 const decl = payload.data;
872880
873881 // offset into the offset table within the 'data' section
......@@ -880,10 +888,32 @@ pub const Context = struct {
880888 try writer.writeByte(wasm.opcode(.i32_load));
881889 try leb.writeULEB128(writer, @as(u32, 0));
882890 try leb.writeULEB128(writer, @as(u32, 0));
883 } else return self.fail(inst.base.src, "Wasm TODO: emitConstant for other const pointer tag {s}", .{inst.val.tag()});
891 } else return self.fail(src, "Wasm TODO: emitConstant for other const pointer tag {s}", .{value.tag()});
884892 },
885893 .Void => {},
886 else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{ty}),
894 .Enum => {
895 if (value.castTag(.enum_field_index)) |field_index| {
896 switch (ty.tag()) {
897 .enum_simple => {
898 try writer.writeByte(wasm.opcode(.i32_const));
899 try leb.writeULEB128(writer, field_index.data);
900 },
901 .enum_full, .enum_nonexhaustive => {
902 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
903 if (enum_full.values.count() != 0) {
904 const tag_val = enum_full.values.entries.items[field_index.data].key;
905 try self.emitConstant(src, tag_val, enum_full.tag_ty);
906 }
907 },
908 else => unreachable,
909 }
910 } else {
911 var int_tag_buffer: Type.Payload.Bits = undefined;
912 const int_tag_ty = ty.intTagType(&int_tag_buffer);
913 try self.emitConstant(src, value, int_tag_ty);
914 }
915 },
916 else => |zig_type| return self.fail(src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
887917 }
888918 }
889919
......@@ -984,6 +1014,13 @@ pub const Context = struct {
9841014 try self.emitWValue(lhs);
9851015 try self.emitWValue(rhs);
9861016
1017 const signedness: std.builtin.Signedness = blk: {
1018 // by default we tell the operand type is unsigned (i.e. bools and enum values)
1019 if (inst.lhs.ty.zigTypeTag() != .Int) break :blk .unsigned;
1020
1021 // incase of an actual integer, we emit the correct signedness
1022 break :blk inst.lhs.ty.intInfo(self.target).signedness;
1023 };
9871024 const opcode: wasm.Opcode = buildOpcode(.{
9881025 .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty),
9891026 .op = switch (op) {
......@@ -994,7 +1031,7 @@ pub const Context = struct {
9941031 .gte => .ge,
9951032 .gt => .gt,
9961033 },
997 .signedness = inst.lhs.ty.intInfo(self.target).signedness,
1034 .signedness = signedness,
9981035 });
9991036 try self.code.append(wasm.opcode(opcode));
10001037 return WValue{ .code_offset = offset };
......@@ -1045,4 +1082,8 @@ pub const Context = struct {
10451082 try self.code.append(wasm.opcode(.@"unreachable"));
10461083 return .none;
10471084 }
1085
1086 fn genBitcast(self: *Context, bitcast: *Inst.UnOp) InnerError!WValue {
1087 return self.resolveInst(bitcast.operand);
1088 }
10481089};