authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-22 03:08:50-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-22 03:08:50-04:00
logd58233b3612fe2d6bdd00859973aabdeb8c8574b
tree3141c46bf7804e34255548746510ac0326c55bfb
parentb1a86040dd530fd5632b22359eb7534dc2e3f4c9

ir: improve ZIR emission enough to emit hello world


4 files changed, 169 insertions(+), 14 deletions(-)

src-self-hosted/ir.zig+1-2
...@@ -666,8 +666,7 @@ const Analyze = struct {...@@ -666,8 +666,7 @@ const Analyze = struct {
666 };666 };
667667
668 fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {668 fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
669 // As a shortcut, if the small tags / addresses match, we're done.669 if (dest_type.eql(src_type))
670 if (dest_type.tag_if_small_enough == src_type.tag_if_small_enough)
671 return .ok;670 return .ok;
672671
673 // TODO: implement more of this function672 // TODO: implement more of this function
src-self-hosted/ir/text.zig+62-11
...@@ -356,16 +356,15 @@ pub const Module = struct {...@@ -356,16 +356,15 @@ pub const Module = struct {
356 comptime var need_comma = pos_fields.len != 0;356 comptime var need_comma = pos_fields.len != 0;
357 const KW_Args = @TypeOf(inst.kw_args);357 const KW_Args = @TypeOf(inst.kw_args);
358 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {358 inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
359 if (need_comma) {
360 try stream.writeAll(", ");
361 }
362 if (@typeInfo(arg_field.field_type) == .Optional) {359 if (@typeInfo(arg_field.field_type) == .Optional) {
363 if (@field(inst.kw_args, arg_field.name)) |non_optional| {360 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
361 if (need_comma) try stream.writeAll(", ");
364 try stream.print("{}=", .{arg_field.name});362 try stream.print("{}=", .{arg_field.name});
365 try self.writeParamToStream(stream, non_optional, inst_table);363 try self.writeParamToStream(stream, non_optional, inst_table);
366 need_comma = true;364 need_comma = true;
367 }365 }
368 } else {366 } else {
367 if (need_comma) try stream.writeAll(", ");
369 try stream.print("{}=", .{arg_field.name});368 try stream.print("{}=", .{arg_field.name});
370 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);369 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);
371 need_comma = true;370 need_comma = true;
...@@ -806,7 +805,7 @@ const EmitZIR = struct {...@@ -806,7 +805,7 @@ const EmitZIR = struct {
806 decls: std.ArrayList(*Inst),805 decls: std.ArrayList(*Inst),
807 decl_table: std.AutoHashMap(*ir.Inst, *Inst),806 decl_table: std.AutoHashMap(*ir.Inst, *Inst),
808807
809 pub fn emit(self: *EmitZIR) !void {808 fn emit(self: *EmitZIR) !void {
810 for (self.old_module.exports) |module_export| {809 for (self.old_module.exports) |module_export| {
811 const export_value = try self.emitTypedValue(module_export.src, module_export.typed_value);810 const export_value = try self.emitTypedValue(module_export.src, module_export.typed_value);
812 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.name);811 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.name);
...@@ -823,7 +822,7 @@ const EmitZIR = struct {...@@ -823,7 +822,7 @@ const EmitZIR = struct {
823 }822 }
824 }823 }
825824
826 pub fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {825 fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
827 if (inst.cast(ir.Inst.Constant)) |const_inst| {826 if (inst.cast(ir.Inst.Constant)) |const_inst| {
828 if (self.decl_table.getValue(inst)) |decl| {827 if (self.decl_table.getValue(inst)) |decl| {
829 return decl;828 return decl;
...@@ -836,7 +835,20 @@ const EmitZIR = struct {...@@ -836,7 +835,20 @@ const EmitZIR = struct {
836 }835 }
837 }836 }
838837
839 pub fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: ir.TypedValue) Allocator.Error!*Inst {838 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
839 const int_inst = try self.arena.allocator.create(Inst.Int);
840 int_inst.* = .{
841 .base = .{ .src = src, .tag = Inst.Int.base_tag },
842 .positionals = .{
843 .int = try val.toBigInt(&self.arena.allocator),
844 },
845 .kw_args = .{},
846 };
847 try self.decls.append(&int_inst.base);
848 return &int_inst.base;
849 }
850
851 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: ir.TypedValue) Allocator.Error!*Inst {
840 switch (typed_value.ty.zigTypeTag()) {852 switch (typed_value.ty.zigTypeTag()) {
841 .Pointer => {853 .Pointer => {
842 const ptr_elem_type = typed_value.ty.elemType();854 const ptr_elem_type = typed_value.ty.elemType();
...@@ -854,6 +866,21 @@ const EmitZIR = struct {...@@ -854,6 +866,21 @@ const EmitZIR = struct {
854 else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),866 else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),
855 }867 }
856 },868 },
869 .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val),
870 .Int => {
871 const as_inst = try self.arena.allocator.create(Inst.As);
872 as_inst.* = .{
873 .base = .{ .src = src, .tag = Inst.As.base_tag },
874 .positionals = .{
875 .dest_type = try self.emitType(src, typed_value.ty),
876 .value = try self.emitComptimeIntVal(src, typed_value.val),
877 },
878 .kw_args = .{},
879 };
880 try self.decls.append(&as_inst.base);
881
882 return &as_inst.base;
883 },
857 .Type => {884 .Type => {
858 const ty = typed_value.val.toType();885 const ty = typed_value.val.toType();
859 return self.emitType(src, ty);886 return self.emitType(src, ty);
...@@ -883,14 +910,38 @@ const EmitZIR = struct {...@@ -883,14 +910,38 @@ const EmitZIR = struct {
883 .assembly => blk: {910 .assembly => blk: {
884 const old_inst = inst.cast(ir.Inst.Assembly).?;911 const old_inst = inst.cast(ir.Inst.Assembly).?;
885 const new_inst = try self.arena.allocator.create(Inst.Asm);912 const new_inst = try self.arena.allocator.create(Inst.Asm);
913
914 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
915 for (inputs) |*elem, i| {
916 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
917 }
918
919 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
920 for (clobbers) |*elem, i| {
921 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
922 }
923
924 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
925 for (args) |*elem, i| {
926 elem.* = try self.resolveInst(&inst_table, old_inst.args.args[i]);
927 }
928
886 new_inst.* = .{929 new_inst.* = .{
887 .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },930 .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },
888 .positionals = .{931 .positionals = .{
889 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),932 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
890 .return_type = try self.emitType(inst.src, inst.ty),933 .return_type = try self.emitType(inst.src, inst.ty),
891 },934 },
892 // TODO emit more kw_args935 .kw_args = .{
893 .kw_args = .{},936 .@"volatile" = old_inst.args.is_volatile,
937 .output = if (old_inst.args.output) |o|
938 try self.emitStringLiteral(inst.src, o)
939 else
940 null,
941 .inputs = inputs,
942 .clobbers = clobbers,
943 .args = args,
944 },
894 };945 };
895 break :blk &new_inst.base;946 break :blk &new_inst.base;
896 },947 },
...@@ -931,7 +982,7 @@ const EmitZIR = struct {...@@ -931,7 +982,7 @@ const EmitZIR = struct {
931 }982 }
932 }983 }
933984
934 pub fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {985 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
935 switch (ty.tag()) {986 switch (ty.tag()) {
936 .isize => return self.emitPrimitiveType(src, .isize),987 .isize => return self.emitPrimitiveType(src, .isize),
937 .usize => return self.emitPrimitiveType(src, .usize),988 .usize => return self.emitPrimitiveType(src, .usize),
...@@ -986,7 +1037,7 @@ const EmitZIR = struct {...@@ -986,7 +1037,7 @@ const EmitZIR = struct {
986 }1037 }
987 }1038 }
9881039
989 pub fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {1040 fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {
990 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);1041 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
991 primitive_inst.* = .{1042 primitive_inst.* = .{
992 .base = .{ .src = src, .tag = Inst.Primitive.base_tag },1043 .base = .{ .src = src, .tag = Inst.Primitive.base_tag },
...@@ -999,7 +1050,7 @@ const EmitZIR = struct {...@@ -999,7 +1050,7 @@ const EmitZIR = struct {
999 return &primitive_inst.base;1050 return &primitive_inst.base;
1000 }1051 }
10011052
1002 pub fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {1053 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {
1003 const str_inst = try self.arena.allocator.create(Inst.Str);1054 const str_inst = try self.arena.allocator.create(Inst.Str);
1004 str_inst.* = .{1055 str_inst.* = .{
1005 .base = .{ .src = src, .tag = Inst.Str.base_tag },1056 .base = .{ .src = src, .tag = Inst.Str.base_tag },
src-self-hosted/type.zig+54
...@@ -88,6 +88,60 @@ pub const Type = extern union {...@@ -88,6 +88,60 @@ pub const Type = extern union {
88 return @fieldParentPtr(T, "base", self.ptr_otherwise);88 return @fieldParentPtr(T, "base", self.ptr_otherwise);
89 }89 }
9090
91 pub fn eql(self: Type, other: Type) bool {
92 //std.debug.warn("test {} == {}\n", .{ self, other });
93 // As a shortcut, if the small tags / addresses match, we're done.
94 if (self.tag_if_small_enough == other.tag_if_small_enough)
95 return true;
96 const zig_tag_a = self.zigTypeTag();
97 const zig_tag_b = self.zigTypeTag();
98 if (zig_tag_a != zig_tag_b)
99 return false;
100 switch (zig_tag_a) {
101 .Type => return true,
102 .Void => return true,
103 .Bool => return true,
104 .NoReturn => return true,
105 .ComptimeFloat => return true,
106 .ComptimeInt => return true,
107 .Undefined => return true,
108 .Null => return true,
109 .Pointer => {
110 const is_slice_a = isSlice(self);
111 const is_slice_b = isSlice(other);
112 if (is_slice_a != is_slice_b)
113 return false;
114 @panic("TODO implement more pointer Type equality comparison");
115 },
116 .Int => {
117 if (self.tag() != other.tag()) {
118 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
119 return false;
120 }
121 // The target will not be branched upon, because we handled target-dependent cases above.
122 const info_a = self.intInfo(@as(Target, undefined));
123 const info_b = self.intInfo(@as(Target, undefined));
124 return info_a.signed == info_b.signed and info_a.bits == info_b.bits;
125 },
126 .Float,
127 .Array,
128 .Struct,
129 .Optional,
130 .ErrorUnion,
131 .ErrorSet,
132 .Enum,
133 .Union,
134 .Fn,
135 .BoundFn,
136 .Opaque,
137 .Frame,
138 .AnyFrame,
139 .Vector,
140 .EnumLiteral,
141 => @panic("TODO implement more Type equality comparison"),
142 }
143 }
144
91 pub fn format(145 pub fn format(
92 self: Type,146 self: Type,
93 comptime fmt: []const u8,147 comptime fmt: []const u8,
src-self-hosted/value.zig+52-1
...@@ -4,6 +4,7 @@ const log2 = std.math.log2;...@@ -4,6 +4,7 @@ const log2 = std.math.log2;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const BigInt = std.math.big.Int;5const BigInt = std.math.big.Int;
6const Target = std.Target;6const Target = std.Target;
7const Allocator = std.mem.Allocator;
78
8/// This is the raw data, with no bookkeeping, no memory awareness,9/// This is the raw data, with no bookkeeping, no memory awareness,
9/// no de-duplication, and no type system awareness.10/// no de-duplication, and no type system awareness.
...@@ -156,7 +157,7 @@ pub const Value = extern union {...@@ -156,7 +157,7 @@ pub const Value = extern union {
156157
157 /// Asserts that the value is representable as an array of bytes.158 /// Asserts that the value is representable as an array of bytes.
158 /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.159 /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
159 pub fn toAllocatedBytes(self: Value, allocator: *std.mem.Allocator) error{OutOfMemory}![]u8 {160 pub fn toAllocatedBytes(self: Value, allocator: *Allocator) Allocator.Error![]u8 {
160 if (self.cast(Payload.Bytes)) |bytes| {161 if (self.cast(Payload.Bytes)) |bytes| {
161 return std.mem.dupe(allocator, u8, bytes.data);162 return std.mem.dupe(allocator, u8, bytes.data);
162 }163 }
...@@ -213,6 +214,56 @@ pub const Value = extern union {...@@ -213,6 +214,56 @@ pub const Value = extern union {
213 };214 };
214 }215 }
215216
217 /// Asserts the value is an integer.
218 pub fn toBigInt(self: Value, allocator: *Allocator) Allocator.Error!BigInt {
219 switch (self.tag()) {
220 .ty,
221 .u8_type,
222 .i8_type,
223 .isize_type,
224 .usize_type,
225 .c_short_type,
226 .c_ushort_type,
227 .c_int_type,
228 .c_uint_type,
229 .c_long_type,
230 .c_ulong_type,
231 .c_longlong_type,
232 .c_ulonglong_type,
233 .c_longdouble_type,
234 .f16_type,
235 .f32_type,
236 .f64_type,
237 .f128_type,
238 .c_void_type,
239 .bool_type,
240 .void_type,
241 .type_type,
242 .anyerror_type,
243 .comptime_int_type,
244 .comptime_float_type,
245 .noreturn_type,
246 .fn_naked_noreturn_no_args_type,
247 .single_const_pointer_to_comptime_int_type,
248 .const_slice_u8_type,
249 .void_value,
250 .noreturn_value,
251 .bool_true,
252 .bool_false,
253 .function,
254 .ref,
255 .ref_val,
256 .bytes,
257 => unreachable,
258
259 .zero => return BigInt.initSet(allocator, 0),
260
261 .int_u64 => return BigInt.initSet(allocator, self.cast(Payload.Int_u64).?.int),
262 .int_i64 => return BigInt.initSet(allocator, self.cast(Payload.Int_i64).?.int),
263 .int_big => return self.cast(Payload.IntBig).?.big_int,
264 }
265 }
266
216 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.267 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
217 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {268 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
218 switch (self.tag()) {269 switch (self.tag()) {