ir: analyze fieldptr instruction

This commit is contained in:
Andrew Kelley
2020-04-21 21:14:56 -04:00
parent c12bc8652e
commit 2c11acf807
3 changed files with 137 additions and 35 deletions

View File

@@ -382,7 +382,7 @@ const Analyze = struct {
return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int);
},
.ptrtoint => return self.analyzeInstPtrToInt(func, old_inst.cast(text.Inst.PtrToInt).?),
.fieldptr => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
.fieldptr => return self.analyzeInstFieldPtr(func, old_inst.cast(text.Inst.FieldPtr).?),
.deref => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
.as => return self.analyzeInstAs(func, old_inst.cast(text.Inst.As).?),
.@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
@@ -470,6 +470,39 @@ const Analyze = struct {
return self.addNewInstArgs(f, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
}
fn analyzeInstFieldPtr(self: *Analyze, func: ?*Fn, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
const object_ptr = try self.resolveInst(func, fieldptr.positionals.object_ptr);
const field_name = try self.resolveConstString(func, fieldptr.positionals.field_name);
const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
.Pointer => object_ptr.ty.elemType(),
else => return self.fail(fieldptr.base.src, "expected pointer, found '{}'", .{object_ptr.ty}),
};
switch (elem_ty.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
const len_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
len_payload.* = .{ .int = elem_ty.arrayLen() };
const ref_payload = try self.arena.allocator.create(Value.Payload.RefVal);
ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
return self.constInst(fieldptr.base.src, .{
.ty = Type.initTag(.single_const_pointer_to_comptime_int),
.val = Value.initPayload(&ref_payload.base),
});
} else {
return self.fail(
fieldptr.positionals.field_name.src,
"no member named '{}' in '{}'",
.{ field_name, elem_ty },
);
}
},
else => return self.fail(fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}),
}
}
fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst {
const dest_type = try self.resolveType(func, intcast.positionals.dest_type);
const new_inst = try self.resolveInst(func, intcast.positionals.value);

View File

@@ -54,6 +54,7 @@ pub const Type = extern union {
.array, .array_u8_sentinel_0 => return .Array,
.single_const_pointer => return .Pointer,
.single_const_pointer_to_comptime_int => return .Pointer,
.const_slice_u8 => return .Pointer,
}
}
@@ -127,6 +128,7 @@ pub const Type = extern union {
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
.fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
.array_u8_sentinel_0 => {
const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
@@ -177,6 +179,7 @@ pub const Type = extern union {
.@"comptime_float" => return Value.initTag(.comptime_float_type),
.@"noreturn" => return Value.initTag(.noreturn_type),
.fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
.single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
.const_slice_u8 => return Value.initTag(.const_slice_u8_type),
else => {
const ty_payload = try allocator.create(Value.Payload.Ty);
@@ -219,7 +222,9 @@ pub const Type = extern union {
.fn_naked_noreturn_no_args,
=> false,
.single_const_pointer => true,
.single_const_pointer,
.single_const_pointer_to_comptime_int,
=> true,
};
}
@@ -253,6 +258,7 @@ pub const Type = extern union {
.array,
.array_u8_sentinel_0,
.single_const_pointer,
.single_const_pointer_to_comptime_int,
.fn_naked_noreturn_no_args,
=> false,
@@ -293,7 +299,10 @@ pub const Type = extern union {
.fn_naked_noreturn_no_args,
=> unreachable,
.single_const_pointer, .const_slice_u8 => true,
.single_const_pointer,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
=> true,
};
}
@@ -330,7 +339,47 @@ pub const Type = extern union {
.array => self.cast(Payload.Array).?.elem_type,
.single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,
.array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.@"u8"),
.array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
.single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
};
}
/// Asserts the type is an array.
pub fn arrayLen(self: Type) u64 {
return switch (self.tag()) {
.u8,
.i8,
.isize,
.usize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.fn_naked_noreturn_no_args,
.single_const_pointer,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
=> unreachable,
.array => self.cast(Payload.Array).?.len,
.array_u8_sentinel_0 => self.cast(Payload.Array_u8_Sentinel0).?.len,
};
}
@@ -353,6 +402,7 @@ pub const Type = extern union {
.fn_naked_noreturn_no_args,
.array,
.single_const_pointer,
.single_const_pointer_to_comptime_int,
.array_u8_sentinel_0,
.const_slice_u8,
=> unreachable,
@@ -380,32 +430,33 @@ pub const Type = extern union {
/// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
pub const Tag = enum {
// The first section of this enum are tags that require no payload.
@"u8",
@"i8",
@"isize",
@"usize",
@"c_short",
@"c_ushort",
@"c_int",
@"c_uint",
@"c_long",
@"c_ulong",
@"c_longlong",
@"c_ulonglong",
@"c_longdouble",
@"c_void",
@"f16",
@"f32",
@"f64",
@"f128",
@"bool",
@"void",
@"type",
@"anyerror",
@"comptime_int",
@"comptime_float",
@"noreturn",
u8,
i8,
isize,
usize,
c_short,
c_ushort,
c_int,
c_uint,
c_long,
c_ulong,
c_longlong,
c_ulonglong,
c_longdouble,
c_void,
f16,
f32,
f64,
f128,
bool,
void,
type,
anyerror,
comptime_int,
comptime_float,
noreturn,
fn_naked_noreturn_no_args,
single_const_pointer_to_comptime_int,
const_slice_u8, // See last_no_payload_tag below.
// After this, the tag requires a payload.

View File

@@ -44,6 +44,7 @@ pub const Value = extern union {
comptime_float_type,
noreturn_type,
fn_naked_noreturn_no_args_type,
single_const_pointer_to_comptime_int_type,
const_slice_u8_type,
void_value,
@@ -58,6 +59,7 @@ pub const Value = extern union {
int_big,
function,
ref,
ref_val,
bytes,
pub const last_no_payload_tag = Tag.bool_false;
@@ -100,7 +102,8 @@ pub const Value = extern union {
out_stream: var,
) !void {
comptime assert(fmt.len == 0);
switch (self.tag()) {
var val = self;
while (true) switch (val.tag()) {
.u8_type => return out_stream.writeAll("u8"),
.i8_type => return out_stream.writeAll("i8"),
.isize_type => return out_stream.writeAll("isize"),
@@ -127,20 +130,26 @@ pub const Value = extern union {
.comptime_float_type => return out_stream.writeAll("comptime_float"),
.noreturn_type => return out_stream.writeAll("noreturn"),
.fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
.const_slice_u8_type => return out_stream.writeAll("[]const u8"),
.void_value => return out_stream.writeAll("{}"),
.noreturn_value => return out_stream.writeAll("unreachable"),
.bool_true => return out_stream.writeAll("true"),
.bool_false => return out_stream.writeAll("false"),
.ty => return self.cast(Payload.Ty).?.ty.format("", options, out_stream),
.int_u64 => return std.fmt.formatIntValue(self.cast(Payload.Int_u64).?.int, "", options, out_stream),
.int_i64 => return std.fmt.formatIntValue(self.cast(Payload.Int_i64).?.int, "", options, out_stream),
.int_big => return out_stream.print("{}", .{self.cast(Payload.IntBig).?.big_int}),
.ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
.int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
.int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),
.int_big => return out_stream.print("{}", .{val.cast(Payload.IntBig).?.big_int}),
.function => return out_stream.writeAll("(function)"),
.ref => return out_stream.writeAll("(ref)"),
.ref_val => {
try out_stream.writeAll("*const ");
val = val.cast(Payload.RefVal).?.val;
continue;
},
.bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
}
};
}
/// Asserts that the value is representable as an array of bytes.
@@ -183,6 +192,7 @@ pub const Value = extern union {
.comptime_float_type => Type.initTag(.@"comptime_float"),
.noreturn_type => Type.initTag(.@"noreturn"),
.fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
.single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
.const_slice_u8_type => Type.initTag(.const_slice_u8),
.void_value,
@@ -194,6 +204,7 @@ pub const Value = extern union {
.int_big,
.function,
.ref,
.ref_val,
.bytes,
=> unreachable,
};
@@ -229,6 +240,7 @@ pub const Value = extern union {
.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,
@@ -236,6 +248,7 @@ pub const Value = extern union {
.bool_false,
.function,
.ref,
.ref_val,
.bytes,
=> unreachable,
@@ -311,6 +324,11 @@ pub const Value = extern union {
pointee: *MemoryCell,
};
pub const RefVal = struct {
base: Payload = Payload{ .tag = .ref_val },
val: Value,
};
pub const Bytes = struct {
base: Payload = Payload{ .tag = .bytes },
data: []const u8,