Commit 97d69e3352
Changed files (10)
test
behavior
src/codegen/llvm/bindings.zig
@@ -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) {
src/codegen/c.zig
@@ -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();
src/codegen/llvm.zig
@@ -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;
src/Air.zig
@@ -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,
src/codegen.zig
@@ -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);
src/Liveness.zig
@@ -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 });
src/print_air.zig
@@ -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,
src/Sema.zig
@@ -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(
test/behavior/array.zig
@@ -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;
+}
test/behavior/array_stage1.zig
@@ -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);