ir: improve ZIR emission enough to emit hello world
parent
b1a86040dd
commit
d58233b361
|
@ -666,8 +666,7 @@ const Analyze = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
|
fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
|
||||||
// As a shortcut, if the small tags / addresses match, we're done.
|
if (dest_type.eql(src_type))
|
||||||
if (dest_type.tag_if_small_enough == src_type.tag_if_small_enough)
|
|
||||||
return .ok;
|
return .ok;
|
||||||
|
|
||||||
// TODO: implement more of this function
|
// TODO: implement more of this function
|
||||||
|
|
|
@ -356,16 +356,15 @@ pub const Module = struct {
|
||||||
comptime var need_comma = pos_fields.len != 0;
|
comptime var need_comma = pos_fields.len != 0;
|
||||||
const KW_Args = @TypeOf(inst.kw_args);
|
const KW_Args = @TypeOf(inst.kw_args);
|
||||||
inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
|
inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
|
||||||
if (need_comma) {
|
|
||||||
try stream.writeAll(", ");
|
|
||||||
}
|
|
||||||
if (@typeInfo(arg_field.field_type) == .Optional) {
|
if (@typeInfo(arg_field.field_type) == .Optional) {
|
||||||
if (@field(inst.kw_args, arg_field.name)) |non_optional| {
|
if (@field(inst.kw_args, arg_field.name)) |non_optional| {
|
||||||
|
if (need_comma) try stream.writeAll(", ");
|
||||||
try stream.print("{}=", .{arg_field.name});
|
try stream.print("{}=", .{arg_field.name});
|
||||||
try self.writeParamToStream(stream, non_optional, inst_table);
|
try self.writeParamToStream(stream, non_optional, inst_table);
|
||||||
need_comma = true;
|
need_comma = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (need_comma) try stream.writeAll(", ");
|
||||||
try stream.print("{}=", .{arg_field.name});
|
try stream.print("{}=", .{arg_field.name});
|
||||||
try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);
|
try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);
|
||||||
need_comma = true;
|
need_comma = true;
|
||||||
|
@ -806,7 +805,7 @@ const EmitZIR = struct {
|
||||||
decls: std.ArrayList(*Inst),
|
decls: std.ArrayList(*Inst),
|
||||||
decl_table: std.AutoHashMap(*ir.Inst, *Inst),
|
decl_table: std.AutoHashMap(*ir.Inst, *Inst),
|
||||||
|
|
||||||
pub fn emit(self: *EmitZIR) !void {
|
fn emit(self: *EmitZIR) !void {
|
||||||
for (self.old_module.exports) |module_export| {
|
for (self.old_module.exports) |module_export| {
|
||||||
const export_value = try self.emitTypedValue(module_export.src, module_export.typed_value);
|
const export_value = try self.emitTypedValue(module_export.src, module_export.typed_value);
|
||||||
const symbol_name = try self.emitStringLiteral(module_export.src, module_export.name);
|
const symbol_name = try self.emitStringLiteral(module_export.src, module_export.name);
|
||||||
|
@ -823,7 +822,7 @@ const EmitZIR = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
|
fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
|
||||||
if (inst.cast(ir.Inst.Constant)) |const_inst| {
|
if (inst.cast(ir.Inst.Constant)) |const_inst| {
|
||||||
if (self.decl_table.getValue(inst)) |decl| {
|
if (self.decl_table.getValue(inst)) |decl| {
|
||||||
return decl;
|
return decl;
|
||||||
|
@ -836,7 +835,20 @@ const EmitZIR = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: ir.TypedValue) Allocator.Error!*Inst {
|
fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
|
||||||
|
const int_inst = try self.arena.allocator.create(Inst.Int);
|
||||||
|
int_inst.* = .{
|
||||||
|
.base = .{ .src = src, .tag = Inst.Int.base_tag },
|
||||||
|
.positionals = .{
|
||||||
|
.int = try val.toBigInt(&self.arena.allocator),
|
||||||
|
},
|
||||||
|
.kw_args = .{},
|
||||||
|
};
|
||||||
|
try self.decls.append(&int_inst.base);
|
||||||
|
return &int_inst.base;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: ir.TypedValue) Allocator.Error!*Inst {
|
||||||
switch (typed_value.ty.zigTypeTag()) {
|
switch (typed_value.ty.zigTypeTag()) {
|
||||||
.Pointer => {
|
.Pointer => {
|
||||||
const ptr_elem_type = typed_value.ty.elemType();
|
const ptr_elem_type = typed_value.ty.elemType();
|
||||||
|
@ -854,6 +866,21 @@ const EmitZIR = struct {
|
||||||
else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),
|
else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val),
|
||||||
|
.Int => {
|
||||||
|
const as_inst = try self.arena.allocator.create(Inst.As);
|
||||||
|
as_inst.* = .{
|
||||||
|
.base = .{ .src = src, .tag = Inst.As.base_tag },
|
||||||
|
.positionals = .{
|
||||||
|
.dest_type = try self.emitType(src, typed_value.ty),
|
||||||
|
.value = try self.emitComptimeIntVal(src, typed_value.val),
|
||||||
|
},
|
||||||
|
.kw_args = .{},
|
||||||
|
};
|
||||||
|
try self.decls.append(&as_inst.base);
|
||||||
|
|
||||||
|
return &as_inst.base;
|
||||||
|
},
|
||||||
.Type => {
|
.Type => {
|
||||||
const ty = typed_value.val.toType();
|
const ty = typed_value.val.toType();
|
||||||
return self.emitType(src, ty);
|
return self.emitType(src, ty);
|
||||||
|
@ -883,14 +910,38 @@ const EmitZIR = struct {
|
||||||
.assembly => blk: {
|
.assembly => blk: {
|
||||||
const old_inst = inst.cast(ir.Inst.Assembly).?;
|
const old_inst = inst.cast(ir.Inst.Assembly).?;
|
||||||
const new_inst = try self.arena.allocator.create(Inst.Asm);
|
const new_inst = try self.arena.allocator.create(Inst.Asm);
|
||||||
|
|
||||||
|
const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
|
||||||
|
for (inputs) |*elem, i| {
|
||||||
|
elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
|
||||||
|
for (clobbers) |*elem, i| {
|
||||||
|
elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
|
||||||
|
for (args) |*elem, i| {
|
||||||
|
elem.* = try self.resolveInst(&inst_table, old_inst.args.args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
new_inst.* = .{
|
new_inst.* = .{
|
||||||
.base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },
|
.base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },
|
||||||
.positionals = .{
|
.positionals = .{
|
||||||
.asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
|
.asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
|
||||||
.return_type = try self.emitType(inst.src, inst.ty),
|
.return_type = try self.emitType(inst.src, inst.ty),
|
||||||
},
|
},
|
||||||
// TODO emit more kw_args
|
.kw_args = .{
|
||||||
.kw_args = .{},
|
.@"volatile" = old_inst.args.is_volatile,
|
||||||
|
.output = if (old_inst.args.output) |o|
|
||||||
|
try self.emitStringLiteral(inst.src, o)
|
||||||
|
else
|
||||||
|
null,
|
||||||
|
.inputs = inputs,
|
||||||
|
.clobbers = clobbers,
|
||||||
|
.args = args,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
break :blk &new_inst.base;
|
break :blk &new_inst.base;
|
||||||
},
|
},
|
||||||
|
@ -931,7 +982,7 @@ const EmitZIR = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
|
fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
|
||||||
switch (ty.tag()) {
|
switch (ty.tag()) {
|
||||||
.isize => return self.emitPrimitiveType(src, .isize),
|
.isize => return self.emitPrimitiveType(src, .isize),
|
||||||
.usize => return self.emitPrimitiveType(src, .usize),
|
.usize => return self.emitPrimitiveType(src, .usize),
|
||||||
|
@ -986,7 +1037,7 @@ const EmitZIR = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {
|
fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst {
|
||||||
const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
|
const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
|
||||||
primitive_inst.* = .{
|
primitive_inst.* = .{
|
||||||
.base = .{ .src = src, .tag = Inst.Primitive.base_tag },
|
.base = .{ .src = src, .tag = Inst.Primitive.base_tag },
|
||||||
|
@ -999,7 +1050,7 @@ const EmitZIR = struct {
|
||||||
return &primitive_inst.base;
|
return &primitive_inst.base;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {
|
fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {
|
||||||
const str_inst = try self.arena.allocator.create(Inst.Str);
|
const str_inst = try self.arena.allocator.create(Inst.Str);
|
||||||
str_inst.* = .{
|
str_inst.* = .{
|
||||||
.base = .{ .src = src, .tag = Inst.Str.base_tag },
|
.base = .{ .src = src, .tag = Inst.Str.base_tag },
|
||||||
|
|
|
@ -88,6 +88,60 @@ pub const Type = extern union {
|
||||||
return @fieldParentPtr(T, "base", self.ptr_otherwise);
|
return @fieldParentPtr(T, "base", self.ptr_otherwise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn eql(self: Type, other: Type) bool {
|
||||||
|
//std.debug.warn("test {} == {}\n", .{ self, other });
|
||||||
|
// As a shortcut, if the small tags / addresses match, we're done.
|
||||||
|
if (self.tag_if_small_enough == other.tag_if_small_enough)
|
||||||
|
return true;
|
||||||
|
const zig_tag_a = self.zigTypeTag();
|
||||||
|
const zig_tag_b = self.zigTypeTag();
|
||||||
|
if (zig_tag_a != zig_tag_b)
|
||||||
|
return false;
|
||||||
|
switch (zig_tag_a) {
|
||||||
|
.Type => return true,
|
||||||
|
.Void => return true,
|
||||||
|
.Bool => return true,
|
||||||
|
.NoReturn => return true,
|
||||||
|
.ComptimeFloat => return true,
|
||||||
|
.ComptimeInt => return true,
|
||||||
|
.Undefined => return true,
|
||||||
|
.Null => return true,
|
||||||
|
.Pointer => {
|
||||||
|
const is_slice_a = isSlice(self);
|
||||||
|
const is_slice_b = isSlice(other);
|
||||||
|
if (is_slice_a != is_slice_b)
|
||||||
|
return false;
|
||||||
|
@panic("TODO implement more pointer Type equality comparison");
|
||||||
|
},
|
||||||
|
.Int => {
|
||||||
|
if (self.tag() != other.tag()) {
|
||||||
|
// Detect that e.g. u64 != usize, even if the bits match on a particular target.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// The target will not be branched upon, because we handled target-dependent cases above.
|
||||||
|
const info_a = self.intInfo(@as(Target, undefined));
|
||||||
|
const info_b = self.intInfo(@as(Target, undefined));
|
||||||
|
return info_a.signed == info_b.signed and info_a.bits == info_b.bits;
|
||||||
|
},
|
||||||
|
.Float,
|
||||||
|
.Array,
|
||||||
|
.Struct,
|
||||||
|
.Optional,
|
||||||
|
.ErrorUnion,
|
||||||
|
.ErrorSet,
|
||||||
|
.Enum,
|
||||||
|
.Union,
|
||||||
|
.Fn,
|
||||||
|
.BoundFn,
|
||||||
|
.Opaque,
|
||||||
|
.Frame,
|
||||||
|
.AnyFrame,
|
||||||
|
.Vector,
|
||||||
|
.EnumLiteral,
|
||||||
|
=> @panic("TODO implement more Type equality comparison"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn format(
|
pub fn format(
|
||||||
self: Type,
|
self: Type,
|
||||||
comptime fmt: []const u8,
|
comptime fmt: []const u8,
|
||||||
|
|
|
@ -4,6 +4,7 @@ const log2 = std.math.log2;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const BigInt = std.math.big.Int;
|
const BigInt = std.math.big.Int;
|
||||||
const Target = std.Target;
|
const Target = std.Target;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
/// This is the raw data, with no bookkeeping, no memory awareness,
|
/// This is the raw data, with no bookkeeping, no memory awareness,
|
||||||
/// no de-duplication, and no type system awareness.
|
/// no de-duplication, and no type system awareness.
|
||||||
|
@ -156,7 +157,7 @@ pub const Value = extern union {
|
||||||
|
|
||||||
/// Asserts that the value is representable as an array of bytes.
|
/// Asserts that the value is representable as an array of bytes.
|
||||||
/// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
|
/// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
|
||||||
pub fn toAllocatedBytes(self: Value, allocator: *std.mem.Allocator) error{OutOfMemory}![]u8 {
|
pub fn toAllocatedBytes(self: Value, allocator: *Allocator) Allocator.Error![]u8 {
|
||||||
if (self.cast(Payload.Bytes)) |bytes| {
|
if (self.cast(Payload.Bytes)) |bytes| {
|
||||||
return std.mem.dupe(allocator, u8, bytes.data);
|
return std.mem.dupe(allocator, u8, bytes.data);
|
||||||
}
|
}
|
||||||
|
@ -213,6 +214,56 @@ pub const Value = extern union {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Asserts the value is an integer.
|
||||||
|
pub fn toBigInt(self: Value, allocator: *Allocator) Allocator.Error!BigInt {
|
||||||
|
switch (self.tag()) {
|
||||||
|
.ty,
|
||||||
|
.u8_type,
|
||||||
|
.i8_type,
|
||||||
|
.isize_type,
|
||||||
|
.usize_type,
|
||||||
|
.c_short_type,
|
||||||
|
.c_ushort_type,
|
||||||
|
.c_int_type,
|
||||||
|
.c_uint_type,
|
||||||
|
.c_long_type,
|
||||||
|
.c_ulong_type,
|
||||||
|
.c_longlong_type,
|
||||||
|
.c_ulonglong_type,
|
||||||
|
.c_longdouble_type,
|
||||||
|
.f16_type,
|
||||||
|
.f32_type,
|
||||||
|
.f64_type,
|
||||||
|
.f128_type,
|
||||||
|
.c_void_type,
|
||||||
|
.bool_type,
|
||||||
|
.void_type,
|
||||||
|
.type_type,
|
||||||
|
.anyerror_type,
|
||||||
|
.comptime_int_type,
|
||||||
|
.comptime_float_type,
|
||||||
|
.noreturn_type,
|
||||||
|
.fn_naked_noreturn_no_args_type,
|
||||||
|
.single_const_pointer_to_comptime_int_type,
|
||||||
|
.const_slice_u8_type,
|
||||||
|
.void_value,
|
||||||
|
.noreturn_value,
|
||||||
|
.bool_true,
|
||||||
|
.bool_false,
|
||||||
|
.function,
|
||||||
|
.ref,
|
||||||
|
.ref_val,
|
||||||
|
.bytes,
|
||||||
|
=> unreachable,
|
||||||
|
|
||||||
|
.zero => return BigInt.initSet(allocator, 0),
|
||||||
|
|
||||||
|
.int_u64 => return BigInt.initSet(allocator, self.cast(Payload.Int_u64).?.int),
|
||||||
|
.int_i64 => return BigInt.initSet(allocator, self.cast(Payload.Int_i64).?.int),
|
||||||
|
.int_big => return self.cast(Payload.IntBig).?.big_int,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
|
/// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
|
||||||
pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
|
pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
|
||||||
switch (self.tag()) {
|
switch (self.tag()) {
|
||||||
|
|
Loading…
Reference in New Issue