stage2: add array_to_slice AIR instruction

This commit is contained in:
Andrew Kelley 2021-09-13 21:37:11 -07:00
parent a9a21c5988
commit 97d69e3352
10 changed files with 120 additions and 92 deletions

View File

@ -306,6 +306,9 @@ pub const Inst = struct {
/// Result type is the element type of the inner pointer operand.
/// Uses the `bin_op` field.
ptr_ptr_elem_val,
/// Given a pointer to an array, return a slice.
/// Uses the `ty_op` field.
array_to_slice,
pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
return switch (op) {
@ -526,6 +529,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.struct_field_ptr_index_1,
.struct_field_ptr_index_2,
.struct_field_ptr_index_3,
.array_to_slice,
=> return air.getRefType(datas[inst].ty_op.ty),
.loop,

View File

@ -287,6 +287,7 @@ fn analyzeInst(
.struct_field_ptr_index_1,
.struct_field_ptr_index_2,
.struct_field_ptr_index_3,
.array_to_slice,
=> {
const o = inst_datas[inst].ty_op;
return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });

View File

@ -8952,7 +8952,8 @@ fn coerceArrayPtrToSlice(
// The comptime Value representation is compatible with both types.
return sema.addConstant(dest_type, val);
}
return sema.mod.fail(&block.base, inst_src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
try sema.requireRuntimeBlock(block, inst_src);
return block.addTyOp(.array_to_slice, dest_type, inst);
}
fn coerceArrayPtrToMany(

View File

@ -856,6 +856,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.store => try self.airStore(inst),
.struct_field_ptr=> try self.airStructFieldPtr(inst),
.struct_field_val=> try self.airStructFieldVal(inst),
.array_to_slice => try self.airArrayToSlice(inst),
.struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
.struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
@ -4761,6 +4762,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
else => return self.fail("TODO implement airArrayToSlice for {}", .{
self.target.cpu.arch,
}),
};
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
// First section of indexes correspond to a set number of constant values.
const ref_int = @enumToInt(inst);

View File

@ -910,6 +910,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
.switch_br => try airSwitchBr(o, inst),
.wrap_optional => try airWrapOptional(o, inst),
.struct_field_ptr => try airStructFieldPtr(o, inst),
.array_to_slice => try airArrayToSlice(o, inst),
.struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0),
.struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1),
@ -1860,6 +1861,23 @@ fn airIsErr(
return local;
}
fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue {
if (o.liveness.isUnused(inst))
return CValue.none;
const inst_ty = o.air.typeOfIndex(inst);
const local = try o.allocLocal(inst_ty, .Const);
const ty_op = o.air.instructions.items(.data)[inst].ty_op;
const writer = o.writer();
const operand = try o.resolveInst(ty_op.operand);
const array_len = o.air.typeOf(ty_op.operand).elemType().arrayLen();
try writer.writeAll(" = { .ptr = ");
try o.writeCValue(writer, operand);
try writer.print(", .len = {d} }};\n", .{array_len});
return local;
}
fn IndentWriter(comptime UnderlyingWriter: type) type {
return struct {
const Self = @This();

View File

@ -1006,28 +1006,29 @@ pub const FuncGen = struct {
.is_err => try self.airIsErr(inst, .NE, false),
.is_err_ptr => try self.airIsErr(inst, .NE, true),
.alloc => try self.airAlloc(inst),
.arg => try self.airArg(inst),
.bitcast => try self.airBitCast(inst),
.bool_to_int=> try self.airBoolToInt(inst),
.block => try self.airBlock(inst),
.br => try self.airBr(inst),
.switch_br => try self.airSwitchBr(inst),
.breakpoint => try self.airBreakpoint(inst),
.call => try self.airCall(inst),
.cond_br => try self.airCondBr(inst),
.intcast => try self.airIntCast(inst),
.trunc => try self.airTrunc(inst),
.floatcast => try self.airFloatCast(inst),
.ptrtoint => try self.airPtrToInt(inst),
.load => try self.airLoad(inst),
.loop => try self.airLoop(inst),
.not => try self.airNot(inst),
.ret => try self.airRet(inst),
.store => try self.airStore(inst),
.assembly => try self.airAssembly(inst),
.slice_ptr => try self.airSliceField(inst, 0),
.slice_len => try self.airSliceField(inst, 1),
.alloc => try self.airAlloc(inst),
.arg => try self.airArg(inst),
.bitcast => try self.airBitCast(inst),
.bool_to_int => try self.airBoolToInt(inst),
.block => try self.airBlock(inst),
.br => try self.airBr(inst),
.switch_br => try self.airSwitchBr(inst),
.breakpoint => try self.airBreakpoint(inst),
.call => try self.airCall(inst),
.cond_br => try self.airCondBr(inst),
.intcast => try self.airIntCast(inst),
.trunc => try self.airTrunc(inst),
.floatcast => try self.airFloatCast(inst),
.ptrtoint => try self.airPtrToInt(inst),
.load => try self.airLoad(inst),
.loop => try self.airLoop(inst),
.not => try self.airNot(inst),
.ret => try self.airRet(inst),
.store => try self.airStore(inst),
.assembly => try self.airAssembly(inst),
.slice_ptr => try self.airSliceField(inst, 0),
.slice_len => try self.airSliceField(inst, 1),
.array_to_slice => try self.airArrayToSlice(inst),
.struct_field_ptr => try self.airStructFieldPtr(inst),
.struct_field_val => try self.airStructFieldVal(inst),
@ -1246,6 +1247,24 @@ pub const FuncGen = struct {
return null;
}
fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst))
return null;
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const array_len = self.air.typeOf(ty_op.operand).elemType().arrayLen();
const usize_llvm_ty = try self.dg.llvmType(Type.initTag(.usize));
const len = usize_llvm_ty.constInt(array_len, .False);
const slice_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));
const indices: [2]*const llvm.Value = .{
usize_llvm_ty.constNull(), usize_llvm_ty.constNull(),
};
const ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
const partial = self.builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr, 0, "");
return self.builder.buildInsertValue(partial, len, 1, "");
}
fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {
if (self.liveness.isUnused(inst))
return null;

View File

@ -484,6 +484,15 @@ pub const Builder = opaque {
DestTy: *const Type,
Name: [*:0]const u8,
) *const Value;
pub const buildInsertValue = LLVMBuildInsertValue;
extern fn LLVMBuildInsertValue(
*const Builder,
AggVal: *const Value,
EltVal: *const Value,
Index: c_uint,
Name: [*:0]const u8,
) *const Value;
};
pub const IntPredicate = enum(c_uint) {

View File

@ -174,6 +174,7 @@ const Writer = struct {
.struct_field_ptr_index_1,
.struct_field_ptr_index_2,
.struct_field_ptr_index_3,
.array_to_slice,
=> try w.writeTyOp(s, inst),
.block,

View File

@ -3,3 +3,27 @@ const testing = std.testing;
const mem = std.mem;
const expect = testing.expect;
const expectEqual = testing.expectEqual;
test "arrays" {
var array: [5]u32 = undefined;
var i: u32 = 0;
while (i < 5) {
array[i] = i + 1;
i = array[i];
}
i = 0;
var accumulator = @as(u32, 0);
while (i < 5) {
accumulator += array[i];
i += 1;
}
try expect(accumulator == 15);
try expect(getArrayLen(&array) == 5);
}
fn getArrayLen(a: []const u32) usize {
return a.len;
}

View File

@ -4,30 +4,6 @@ const mem = std.mem;
const expect = testing.expect;
const expectEqual = testing.expectEqual;
test "arrays" {
var array: [5]u32 = undefined;
var i: u32 = 0;
while (i < 5) {
array[i] = i + 1;
i = array[i];
}
i = 0;
var accumulator = @as(u32, 0);
while (i < 5) {
accumulator += array[i];
i += 1;
}
try expect(accumulator == 15);
try expect(getArrayLen(&array) == 5);
}
fn getArrayLen(a: []const u32) usize {
return a.len;
}
test "array with sentinels" {
const S = struct {
fn doTheTest(is_ct: bool) !void {
@ -64,12 +40,7 @@ test "void arrays" {
}
test "array literal" {
const hex_mult = [_]u16{
4096,
256,
16,
1,
};
const hex_mult = [_]u16{ 4096, 256, 16, 1 };
try expect(hex_mult.len == 4);
try expect(hex_mult[1] == 256);
@ -84,21 +55,10 @@ test "array dot len const expr" {
const ArrayDotLenConstExpr = struct {
y: [some_array.len]u8,
};
const some_array = [_]u8{
0,
1,
2,
3,
};
const some_array = [_]u8{ 0, 1, 2, 3 };
test "nested arrays" {
const array_of_strings = [_][]const u8{
"hello",
"this",
"is",
"my",
"thing",
};
const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
for (array_of_strings) |s, i| {
if (i == 0) try expect(mem.eql(u8, s, "hello"));
if (i == 1) try expect(mem.eql(u8, s, "this"));
@ -109,12 +69,8 @@ test "nested arrays" {
}
var s_array: [8]Sub = undefined;
const Sub = struct {
b: u8,
};
const Str = struct {
a: []Sub,
};
const Sub = struct { b: u8 };
const Str = struct { a: []Sub };
test "set global var array via slice embedded in struct" {
var s = Str{ .a = s_array[0..] };
@ -208,26 +164,10 @@ test "runtime initialize array elem and then implicit cast to slice" {
test "array literal as argument to function" {
const S = struct {
fn entry(two: i32) !void {
try foo(&[_]i32{
1,
2,
3,
});
try foo(&[_]i32{
1,
two,
3,
});
try foo2(true, &[_]i32{
1,
2,
3,
});
try foo2(true, &[_]i32{
1,
two,
3,
});
try foo(&[_]i32{ 1, 2, 3 });
try foo(&[_]i32{ 1, two, 3 });
try foo2(true, &[_]i32{ 1, 2, 3 });
try foo2(true, &[_]i32{ 1, two, 3 });
}
fn foo(x: []const i32) !void {
try expect(x[0] == 1);