diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 28f1f4061f..3e08f1f5bc 100644 --- a/src-self-hosted/ir.zig +++ b/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 fail(self: *Analyze, src_offset: usize, comptime format: []const u8, args: var) InnerError { + 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: 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; diff --git a/src-self-hosted/ir/text.zig b/src-self-hosted/ir/text.zig index d850d2bcff..d00966cdce 100644 --- a/src-self-hosted/ir/text.zig +++ b/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, };