Commit 9a2ea5ca42
src-self-hosted/ir/text.zig
@@ -12,7 +12,8 @@ const BigInt = std.math.big.Int;
/// in-memory, analyzed instructions with types and values.
pub const Inst = struct {
tag: Tag,
- src_offset: usize,
+ /// Byte offset into the source.
+ src: usize,
/// These names are used directly as the instruction names in the text format.
pub const Tag = enum {
@@ -599,7 +600,7 @@ const Parser = struct {
) !*Inst {
const inst_specific = try self.arena.allocator.create(InstType);
inst_specific.base = .{
- .src_offset = self.i,
+ .src = self.i,
.tag = InstType.base_tag,
};
src-self-hosted/ir.zig
@@ -14,7 +14,8 @@ const text = @import("ir/text.zig");
pub const Inst = struct {
tag: Tag,
ty: Type,
- src_offset: usize,
+ /// Byte offset into the source.
+ src: usize,
pub const Tag = enum {
unreach,
@@ -29,6 +30,15 @@ pub const Inst = struct {
return @fieldParentPtr(T, "base", base);
}
+ /// Returns `null` if runtime-known.
+ pub fn value(base: *Inst) ?Value {
+ return switch (base.tag) {
+ .unreach => Value.initTag(.noreturn_value),
+ .constant => base.cast(Constant).?.val,
+ .assembly => null,
+ };
+ }
+
pub const Constant = struct {
pub const base_tag = Tag.constant;
base: Inst,
@@ -156,9 +166,7 @@ const Analyze = struct {
}
fn resolveConstValue(self: *Analyze, base: *Inst) !Value {
- const const_inst = base.cast(Inst.Constant) orelse
- return self.fail(base.src_offset, "unable to resolve comptime value", .{});
- return const_inst.val;
+ return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{});
}
fn resolveConstString(self: *Analyze, old_inst: *text.Inst) ![]u8 {
@@ -176,7 +184,7 @@ const Analyze = struct {
switch (typed_value.ty.zigTypeTag()) {
.Fn => {},
else => return self.fail(
- export_inst.positionals.value.src_offset,
+ export_inst.positionals.value.src,
"unable to export type '{}'",
.{typed_value.ty},
),
@@ -187,7 +195,20 @@ const Analyze = struct {
});
}
- fn constStr(self: *Analyze, src_offset: usize, str: []const u8) !*Inst {
+ fn constInst(self: *Analyze, src: usize, typed_value: TypedValue) !*Inst {
+ const const_inst = try self.arena.allocator.create(Inst.Constant);
+ const_inst.* = .{
+ .base = .{
+ .tag = Inst.Constant.base_tag,
+ .ty = typed_value.ty,
+ .src = src,
+ },
+ .val = typed_value.val,
+ };
+ return &const_inst.base;
+ }
+
+ fn constStr(self: *Analyze, src: usize, str: []const u8) !*Inst {
const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0);
array_payload.* = .{ .len = str.len };
@@ -197,16 +218,10 @@ const Analyze = struct {
const bytes_payload = try self.arena.allocator.create(Value.Payload.Bytes);
bytes_payload.* = .{ .data = str };
- const const_inst = try self.arena.allocator.create(Inst.Constant);
- const_inst.* = .{
- .base = .{
- .tag = Inst.Constant.base_tag,
- .ty = Type.initPayload(&ty_payload.base),
- .src_offset = src_offset,
- },
+ return self.constInst(src, .{
+ .ty = Type.initPayload(&ty_payload.base),
.val = Value.initPayload(&bytes_payload.base),
- };
- return &const_inst.base;
+ });
}
fn analyzeDecl(self: *Analyze, old_inst: *text.Inst) !*Inst {
@@ -215,23 +230,28 @@ const Analyze = struct {
// We can use this reference because Inst.Const's Value is arena-allocated.
// The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.
const bytes = old_inst.cast(text.Inst.Str).?.positionals.bytes;
- return self.constStr(old_inst.src_offset, bytes);
+ return self.constStr(old_inst.src, bytes);
},
- .int => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
- .ptrtoint => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
- .fieldptr => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
- .deref => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
- .as => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
- .@"asm" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
- .@"unreachable" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
- .@"fn" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
- .@"export" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
- .primitive => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
- .fntype => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .int => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .ptrtoint => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .fieldptr => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .deref => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .as => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .@"unreachable" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .@"fn" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .primitive => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
+ .fntype => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
}
}
fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
+ const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
+ if (in_memory_result == .ok) {
+ return self.bitcast(dest_type, inst);
+ }
+
// *[N]T to []T
if (inst.ty.isSinglePointer() and dest_type.isSlice() and
(!inst.ty.pointerIsConst() or dest_type.pointerIsConst()))
@@ -241,17 +261,29 @@ const Analyze = struct {
if (array_type.zigTypeTag() == .Array and
coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
{
- return self.fail(inst.src_offset, "TODO do the type coercion", .{});
+ return self.coerceArrayPtrToSlice(dest_type, inst);
}
}
- return self.fail(inst.src_offset, "TODO implement type coercion", .{});
+ return self.fail(inst.src, "TODO implement type coercion", .{});
+ }
+
+ fn bitcast(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
+ return self.fail(inst.src, "TODO implement bitcast analysis", .{});
+ }
+
+ fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
+ if (inst.value()) |val| {
+ // The comptime Value representation is compatible with both types.
+ return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
+ }
+ return self.fail(inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
}
- fn fail(self: *Analyze, src_offset: usize, comptime format: []const u8, args: var) InnerError {
+ fn fail(self: *Analyze, src: usize, comptime format: []const u8, args: var) InnerError {
@setCold(true);
const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args);
(try self.errors.addOne()).* = .{
- .byte_offset = src_offset,
+ .byte_offset = src,
.msg = msg,
};
return error.AnalysisFail;