Commit e3d638a49e

Emily Bellows <emily.a.bellows@hey.com>
2021-11-03 02:23:41
C backend: while, struct tests, better undefined global handling
1. Function signatures that return a no member struct return void 2. Undefined var decls don't get a value generated for them 3. Don't generate bitcast code if the result isn't used, since bitcast is a pure function. Right now struct handling code generates some weird unused bitcast AIR, and this optimization side steps that issue.
1 parent a7d2157
Changed files (4)
src/codegen/c.zig
@@ -448,7 +448,12 @@ pub const DeclGen = struct {
                 try w.writeAll("ZIG_COLD ");
             }
         }
-        try dg.renderType(w, dg.decl.ty.fnReturnType());
+        const return_ty = dg.decl.ty.fnReturnType();
+        if (return_ty.hasCodeGenBits()) {
+            try dg.renderType(w, return_ty);
+        } else {
+            try w.writeAll("void");
+        }
         try w.writeAll(" ");
         try dg.renderDeclName(dg.decl, w);
         try w.writeAll("(");
@@ -947,6 +952,10 @@ pub fn genDecl(o: *Object) !void {
         }
         try fwd_decl_writer.writeAll(";\n");
 
+        if (variable.init.isUndef()) {
+            return;
+        }
+
         try o.indent_writer.insertNewline();
         const w = o.writer();
         try o.dg.renderType(w, o.dg.decl.ty);
@@ -1886,6 +1895,9 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
 }
 
 fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
+    if (f.liveness.isUnused(inst))
+        return CValue.none;
+
     const ty_op = f.air.instructions.items(.data)[inst].ty_op;
     const operand = try f.resolveInst(ty_op.operand);
 
test/behavior/struct.zig
@@ -32,38 +32,6 @@ fn returnEmptyStructInstance() StructWithNoFields {
     return empty_global_instance;
 }
 
-const StructFoo = struct {
-    a: i32,
-    b: bool,
-    c: f32,
-};
-test "structs" {
-    var foo: StructFoo = undefined;
-    @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo));
-    foo.a += 1;
-    foo.b = foo.a == 1;
-    try testFoo(foo);
-    testMutation(&foo);
-    try expect(foo.c == 100);
-}
-fn testFoo(foo: StructFoo) !void {
-    try expect(foo.b);
-}
-fn testMutation(foo: *StructFoo) void {
-    foo.c = 100;
-}
-
-test "struct byval assign" {
-    var foo1: StructFoo = undefined;
-    var foo2: StructFoo = undefined;
-
-    foo1.a = 1234;
-    foo2.a = 0;
-    try expect(foo2.a == 0);
-    foo2 = foo1;
-    try expect(foo2.a == 1234);
-}
-
 const Node = struct {
     val: Val,
     next: *Node,
@@ -90,65 +58,3 @@ test "call member function directly" {
     const result = MemberFnTestFoo.member(instance);
     try expect(result == 1234);
 }
-
-test "struct point to self" {
-    var root: Node = undefined;
-    root.val.x = 1;
-
-    var node: Node = undefined;
-    node.next = &root;
-    node.val.x = 2;
-
-    root.next = &node;
-
-    try expect(node.next.next.next.val.x == 1);
-}
-
-test "void struct fields" {
-    const foo = VoidStructFieldsFoo{
-        .a = void{},
-        .b = 1,
-        .c = void{},
-    };
-    try expect(foo.b == 1);
-    try expect(@sizeOf(VoidStructFieldsFoo) == 4);
-}
-const VoidStructFieldsFoo = struct {
-    a: void,
-    b: i32,
-    c: void,
-};
-
-test "member functions" {
-    const r = MemberFnRand{ .seed = 1234 };
-    try expect(r.getSeed() == 1234);
-}
-const MemberFnRand = struct {
-    seed: u32,
-    pub fn getSeed(r: *const MemberFnRand) u32 {
-        return r.seed;
-    }
-};
-
-test "return struct byval from function" {
-    const bar = makeBar2(1234, 5678);
-    try expect(bar.y == 5678);
-}
-const Bar = struct {
-    x: i32,
-    y: i32,
-};
-fn makeBar2(x: i32, y: i32) Bar {
-    return Bar{
-        .x = x,
-        .y = y,
-    };
-}
-
-test "return empty struct from fn" {
-    _ = testReturnEmptyStructFromFn();
-}
-const EmptyStruct2 = struct {};
-fn testReturnEmptyStructFromFn() EmptyStruct2 {
-    return EmptyStruct2{};
-}
test/behavior/struct_llvm.zig
@@ -0,0 +1,134 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const native_endian = builtin.target.cpu.arch.endian();
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const expectEqualSlices = std.testing.expectEqualSlices;
+const maxInt = std.math.maxInt;
+
+const StructWithNoFields = struct {
+    fn add(a: i32, b: i32) i32 {
+        return a + b;
+    }
+};
+
+const StructFoo = struct {
+    a: i32,
+    b: bool,
+    c: f32,
+};
+test "structs" {
+    var foo: StructFoo = undefined;
+    @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo));
+    foo.a += 1;
+    foo.b = foo.a == 1;
+    try testFoo(foo);
+    testMutation(&foo);
+    try expect(foo.c == 100);
+}
+fn testFoo(foo: StructFoo) !void {
+    try expect(foo.b);
+}
+fn testMutation(foo: *StructFoo) void {
+    foo.c = 100;
+}
+
+test "struct byval assign" {
+    var foo1: StructFoo = undefined;
+    var foo2: StructFoo = undefined;
+
+    foo1.a = 1234;
+    foo2.a = 0;
+    try expect(foo2.a == 0);
+    foo2 = foo1;
+    try expect(foo2.a == 1234);
+}
+
+const Node = struct {
+    val: Val,
+    next: *Node,
+};
+
+const Val = struct {
+    x: i32,
+};
+
+test "struct initializer" {
+    const val = Val{ .x = 42 };
+    try expect(val.x == 42);
+}
+
+const MemberFnTestFoo = struct {
+    x: i32,
+    fn member(foo: MemberFnTestFoo) i32 {
+        return foo.x;
+    }
+};
+
+test "call member function directly" {
+    const instance = MemberFnTestFoo{ .x = 1234 };
+    const result = MemberFnTestFoo.member(instance);
+    try expect(result == 1234);
+}
+
+test "struct point to self" {
+    var root: Node = undefined;
+    root.val.x = 1;
+
+    var node: Node = undefined;
+    node.next = &root;
+    node.val.x = 2;
+
+    root.next = &node;
+
+    try expect(node.next.next.next.val.x == 1);
+}
+
+test "void struct fields" {
+    const foo = VoidStructFieldsFoo{
+        .a = void{},
+        .b = 1,
+        .c = void{},
+    };
+    try expect(foo.b == 1);
+    try expect(@sizeOf(VoidStructFieldsFoo) == 4);
+}
+const VoidStructFieldsFoo = struct {
+    a: void,
+    b: i32,
+    c: void,
+};
+
+test "member functions" {
+    const r = MemberFnRand{ .seed = 1234 };
+    try expect(r.getSeed() == 1234);
+}
+const MemberFnRand = struct {
+    seed: u32,
+    pub fn getSeed(r: *const MemberFnRand) u32 {
+        return r.seed;
+    }
+};
+
+test "return struct byval from function" {
+    const bar = makeBar2(1234, 5678);
+    try expect(bar.y == 5678);
+}
+const Bar = struct {
+    x: i32,
+    y: i32,
+};
+fn makeBar2(x: i32, y: i32) Bar {
+    return Bar{
+        .x = x,
+        .y = y,
+    };
+}
+
+test "return empty struct from fn" {
+    _ = testReturnEmptyStructFromFn();
+}
+const EmptyStruct2 = struct {};
+fn testReturnEmptyStructFromFn() EmptyStruct2 {
+    return EmptyStruct2{};
+}
test/behavior.zig
@@ -21,12 +21,15 @@ test {
     _ = @import("behavior/hasdecl.zig");
     _ = @import("behavior/hasfield.zig");
     _ = @import("behavior/if.zig");
+    _ = @import("behavior/struct.zig");
+    _ = @import("behavior/truncate.zig");
     _ = @import("behavior/null.zig");
     _ = @import("behavior/ptrcast.zig");
     _ = @import("behavior/pub_enum.zig");
     _ = @import("behavior/truncate.zig");
     _ = @import("behavior/underscore.zig");
     _ = @import("behavior/usingnamespace.zig");
+    _ = @import("behavior/while.zig");
 
     if (builtin.object_format != .c) {
         // Tests that pass for stage1 and stage2 but not the C backend.
@@ -60,12 +63,11 @@ test {
         _ = @import("behavior/saturating_arithmetic.zig");
         _ = @import("behavior/sizeof_and_typeof.zig");
         _ = @import("behavior/slice.zig");
-        _ = @import("behavior/struct.zig");
+        _ = @import("behavior/struct_llvm.zig");
         _ = @import("behavior/switch.zig");
         _ = @import("behavior/this.zig");
         _ = @import("behavior/translate_c_macros.zig");
         _ = @import("behavior/union.zig");
-        _ = @import("behavior/while.zig");
         _ = @import("behavior/widening.zig");
 
         if (builtin.zig_is_stage2) {