Merge pull request #11217 from jmc-88/cbe-tuples

CBE: add support for tuples
This commit is contained in:
Andrew Kelley 2022-03-18 15:23:40 -04:00 committed by GitHub
commit e60c0468aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 97 additions and 26 deletions

View file

@ -1000,6 +1000,46 @@ pub const DeclGen = struct {
return name; return name;
} }
fn renderTupleTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
const tuple = t.tupleFields();
var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
defer buffer.deinit();
const writer = buffer.writer();
try buffer.appendSlice("typedef struct {\n");
{
for (tuple.types) |field_ty, i| {
const val = tuple.values[i];
if (val.tag() != .unreachable_value) continue;
var name = std.ArrayList(u8).init(dg.gpa);
defer name.deinit();
try name.writer().print("field_{d}", .{i});
try buffer.append(' ');
try dg.renderTypeAndName(writer, field_ty, .{ .bytes = name.items }, .Mut, Value.initTag(.abi_align_default));
try buffer.appendSlice(";\n");
}
}
try buffer.appendSlice("} ");
const name_start = buffer.items.len;
try writer.print("zig_T_{};\n", .{typeToCIdentifier(t)});
const rendered = buffer.toOwnedSlice();
errdefer dg.typedefs.allocator.free(rendered);
const name = rendered[name_start .. rendered.len - 2];
try dg.typedefs.ensureUnusedCapacity(1);
dg.typedefs.putAssumeCapacityNoClobber(
try t.copy(dg.typedefs_arena),
.{ .name = name, .rendered = rendered },
);
return name;
}
fn renderUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { fn renderUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
const union_ty = t.cast(Type.Payload.Union).?.data; const union_ty = t.cast(Type.Payload.Union).?.data;
const fqn = try union_ty.getFullyQualifiedName(dg.typedefs.allocator); const fqn = try union_ty.getFullyQualifiedName(dg.typedefs.allocator);
@ -1276,7 +1316,9 @@ pub const DeclGen = struct {
return w.writeAll(name); return w.writeAll(name);
}, },
.Struct => { .Struct => {
const name = dg.getTypedefName(t) orelse const name = dg.getTypedefName(t) orelse if (t.isTuple())
try dg.renderTupleTypedef(t)
else
try dg.renderStructTypedef(t); try dg.renderStructTypedef(t);
return w.writeAll(name); return w.writeAll(name);
@ -3116,6 +3158,8 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
var field_name: []const u8 = undefined; var field_name: []const u8 = undefined;
var field_val_ty: Type = undefined; var field_val_ty: Type = undefined;
var buf = std.ArrayList(u8).init(f.object.dg.gpa);
defer buf.deinit();
switch (struct_ty.tag()) { switch (struct_ty.tag()) {
.@"struct" => { .@"struct" => {
const fields = struct_ty.structFields(); const fields = struct_ty.structFields();
@ -3127,6 +3171,14 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
field_name = fields.keys()[index]; field_name = fields.keys()[index];
field_val_ty = fields.values()[index].ty; field_val_ty = fields.values()[index].ty;
}, },
.tuple => {
const tuple = struct_ty.tupleFields();
if (tuple.values[index].tag() != .unreachable_value) return CValue.none;
try buf.writer().print("field_{d}", .{index});
field_name = buf.items;
field_val_ty = tuple.types[index];
},
else => unreachable, else => unreachable,
} }
const payload = if (struct_ty.tag() == .union_tagged) "payload." else ""; const payload = if (struct_ty.tag() == .union_tagged) "payload." else "";
@ -3149,9 +3201,18 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
const writer = f.object.writer(); const writer = f.object.writer();
const struct_byval = try f.resolveInst(extra.struct_operand); const struct_byval = try f.resolveInst(extra.struct_operand);
const struct_ty = f.air.typeOf(extra.struct_operand); const struct_ty = f.air.typeOf(extra.struct_operand);
var buf = std.ArrayList(u8).init(f.object.dg.gpa);
defer buf.deinit();
const field_name = switch (struct_ty.tag()) { const field_name = switch (struct_ty.tag()) {
.@"struct" => struct_ty.structFields().keys()[extra.field_index], .@"struct" => struct_ty.structFields().keys()[extra.field_index],
.@"union", .union_tagged => struct_ty.unionFields().keys()[extra.field_index], .@"union", .union_tagged => struct_ty.unionFields().keys()[extra.field_index],
.tuple => blk: {
const tuple = struct_ty.tupleFields();
if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none;
try buf.writer().print("field_{d}", .{extra.field_index});
break :blk buf.items;
},
else => unreachable, else => unreachable,
}; };
const payload = if (struct_ty.tag() == .union_tagged) "payload." else ""; const payload = if (struct_ty.tag() == .union_tagged) "payload." else "";
@ -3652,11 +3713,25 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
const writer = f.object.writer(); const writer = f.object.writer();
const local = try f.allocLocal(inst_ty, .Const); const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = "); try writer.writeAll(" = {");
switch (vector_ty.zigTypeTag()) {
.Struct => {
const tuple = vector_ty.tupleFields();
var i: usize = 0;
for (elements) |elem, elem_index| {
if (tuple.values[elem_index].tag() != .unreachable_value) continue;
_ = elements; const value = try f.resolveInst(elem);
_ = local; if (i != 0) try writer.writeAll(", ");
return f.fail("TODO: C backend: implement airAggregateInit", .{}); try f.writeCValue(writer, value);
i += 1;
}
},
else => |tag| return f.fail("TODO: C backend: implement airAggregateInit for type {s}", .{@tagName(tag)}),
}
try writer.writeAll("};\n");
return local;
} }
fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {

View file

@ -146,6 +146,7 @@ test {
{ {
// Tests that pass for stage1, llvm backend, C backend // Tests that pass for stage1, llvm backend, C backend
_ = @import("behavior/bugs/421.zig"); _ = @import("behavior/bugs/421.zig");
_ = @import("behavior/bugs/3779.zig");
_ = @import("behavior/bugs/9584.zig"); _ = @import("behavior/bugs/9584.zig");
_ = @import("behavior/cast_int.zig"); _ = @import("behavior/cast_int.zig");
_ = @import("behavior/eval.zig"); _ = @import("behavior/eval.zig");
@ -162,7 +163,6 @@ test {
_ = @import("behavior/saturating_arithmetic.zig"); _ = @import("behavior/saturating_arithmetic.zig");
_ = @import("behavior/widening.zig"); _ = @import("behavior/widening.zig");
_ = @import("behavior/bugs/2114.zig"); _ = @import("behavior/bugs/2114.zig");
_ = @import("behavior/bugs/3779.zig");
_ = @import("behavior/bugs/10147.zig"); _ = @import("behavior/bugs/10147.zig");
_ = @import("behavior/shuffle.zig"); _ = @import("behavior/shuffle.zig");

View file

@ -7,9 +7,9 @@ const ptr_tag_name: [*:0]const u8 = tag_name;
test "@tagName() returns a string literal" { test "@tagName() returns a string literal" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong
try std.testing.expectEqual(*const [13:0]u8, @TypeOf(tag_name)); try std.testing.expect(*const [13:0]u8 == @TypeOf(tag_name));
try std.testing.expectEqualStrings("TestEnumValue", tag_name); try std.testing.expect(std.mem.eql(u8, "TestEnumValue", tag_name));
try std.testing.expectEqualStrings("TestEnumValue", ptr_tag_name[0..tag_name.len]); try std.testing.expect(std.mem.eql(u8, "TestEnumValue", ptr_tag_name[0..tag_name.len]));
} }
const TestError = error{TestErrorCode}; const TestError = error{TestErrorCode};
@ -18,9 +18,9 @@ const ptr_error_name: [*:0]const u8 = error_name;
test "@errorName() returns a string literal" { test "@errorName() returns a string literal" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong
try std.testing.expectEqual(*const [13:0]u8, @TypeOf(error_name)); try std.testing.expect(*const [13:0]u8 == @TypeOf(error_name));
try std.testing.expectEqualStrings("TestErrorCode", error_name); try std.testing.expect(std.mem.eql(u8, "TestErrorCode", error_name));
try std.testing.expectEqualStrings("TestErrorCode", ptr_error_name[0..error_name.len]); try std.testing.expect(std.mem.eql(u8, "TestErrorCode", ptr_error_name[0..error_name.len]));
} }
const TestType = struct {}; const TestType = struct {};
@ -29,9 +29,9 @@ const ptr_type_name: [*:0]const u8 = type_name;
test "@typeName() returns a string literal" { test "@typeName() returns a string literal" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong
try std.testing.expectEqual(*const [type_name.len:0]u8, @TypeOf(type_name)); try std.testing.expect(*const [type_name.len:0]u8 == @TypeOf(type_name));
try std.testing.expectEqualStrings("behavior.bugs.3779.TestType", type_name); try std.testing.expect(std.mem.eql(u8, "behavior.bugs.3779.TestType", type_name));
try std.testing.expectEqualStrings("behavior.bugs.3779.TestType", ptr_type_name[0..type_name.len]); try std.testing.expect(std.mem.eql(u8, "behavior.bugs.3779.TestType", ptr_type_name[0..type_name.len]));
} }
const actual_contents = @embedFile("3779_file_to_embed.txt"); const actual_contents = @embedFile("3779_file_to_embed.txt");
@ -39,10 +39,10 @@ const ptr_actual_contents: [*:0]const u8 = actual_contents;
const expected_contents = "hello zig\n"; const expected_contents = "hello zig\n";
test "@embedFile() returns a string literal" { test "@embedFile() returns a string literal" {
try std.testing.expectEqual(*const [expected_contents.len:0]u8, @TypeOf(actual_contents)); try std.testing.expect(*const [expected_contents.len:0]u8 == @TypeOf(actual_contents));
try std.testing.expect(std.mem.eql(u8, expected_contents, actual_contents)); try std.testing.expect(std.mem.eql(u8, expected_contents, actual_contents));
try std.testing.expectEqualStrings(expected_contents, actual_contents); try std.testing.expect(std.mem.eql(u8, expected_contents, actual_contents));
try std.testing.expectEqualStrings(expected_contents, ptr_actual_contents[0..actual_contents.len]); try std.testing.expect(std.mem.eql(u8, expected_contents, ptr_actual_contents[0..actual_contents.len]));
} }
fn testFnForSrc() std.builtin.SourceLocation { fn testFnForSrc() std.builtin.SourceLocation {
@ -51,9 +51,9 @@ fn testFnForSrc() std.builtin.SourceLocation {
test "@src() returns a struct containing 0-terminated string slices" { test "@src() returns a struct containing 0-terminated string slices" {
const src = testFnForSrc(); const src = testFnForSrc();
try std.testing.expectEqual([:0]const u8, @TypeOf(src.file)); try std.testing.expect([:0]const u8 == @TypeOf(src.file));
try std.testing.expect(std.mem.endsWith(u8, src.file, "3779.zig")); try std.testing.expect(std.mem.endsWith(u8, src.file, "3779.zig"));
try std.testing.expectEqual([:0]const u8, @TypeOf(src.fn_name)); try std.testing.expect([:0]const u8 == @TypeOf(src.fn_name));
try std.testing.expect(std.mem.endsWith(u8, src.fn_name, "testFnForSrc")); try std.testing.expect(std.mem.endsWith(u8, src.fn_name, "testFnForSrc"));
const ptr_src_file: [*:0]const u8 = src.file; const ptr_src_file: [*:0]const u8 = src.file;

View file

@ -2,11 +2,9 @@ const builtin = @import("builtin");
const std = @import("std"); const std = @import("std");
const testing = std.testing; const testing = std.testing;
const expect = testing.expect; const expect = testing.expect;
const expectEqual = testing.expectEqual;
test "tuple concatenation" { test "tuple concatenation" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
@ -18,8 +16,8 @@ test "tuple concatenation" {
var x = .{a}; var x = .{a};
var y = .{b}; var y = .{b};
var c = x ++ y; var c = x ++ y;
try expectEqual(@as(i32, 1), c[0]); try expect(@as(i32, 1) == c[0]);
try expectEqual(@as(i32, 2), c[1]); try expect(@as(i32, 2) == c[1]);
} }
}; };
try S.doTheTest(); try S.doTheTest();
@ -51,7 +49,6 @@ test "tuple multiplication" {
test "more tuple concatenation" { test "more tuple concatenation" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
@ -129,7 +126,6 @@ test "tuple initializer for var" {
} }
test "array-like initializer for tuple types" { test "array-like initializer for tuple types" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO