Commit 98009a2f66

Andrew Kelley <andrew@ziglang.org>
2021-10-29 02:41:45
C backend: implement trunc instruction
Note that there is not any test coverage yet for integer truncation involving non-power-of-two integers.
1 parent 5479c0f
Changed files (5)
src/codegen/c.zig
@@ -1089,7 +1089,6 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .call             => try airCall(f, inst),
             .dbg_stmt         => try airDbgStmt(f, inst),
             .intcast          => try airIntCast(f, inst),
-            .trunc            => try airTrunc(f, inst),
             .bool_to_int      => try airBoolToInt(f, inst),
             .load             => try airLoad(f, inst),
             .ret              => try airRet(f, inst),
@@ -1117,6 +1116,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .float_to_int,
             .fptrunc,
             .fpext,
+            .trunc,
             => try airSimpleCast(f, inst),
 
             .ptrtoint => try airPtrToInt(f, inst),
@@ -1366,16 +1366,6 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
     return local;
 }
 
-fn airTrunc(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);
-    _ = operand;
-    return f.fail("TODO: C backend: airTrunc", .{});
-}
-
 fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
     if (f.liveness.isUnused(inst))
         return CValue.none;
src/codegen/llvm.zig
@@ -3153,8 +3153,7 @@ pub const FuncGen = struct {
     }
 
     fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
-        if (self.liveness.isUnused(inst))
-            return null;
+        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);
test/behavior/basic.zig
@@ -21,456 +21,3 @@ test "truncate" {
 fn testTruncate(x: u32) u8 {
     return @truncate(u8, x);
 }
-
-const g1: i32 = 1233 + 1;
-var g2: i32 = 0;
-
-test "global variables" {
-    try expect(g2 == 0);
-    g2 = g1;
-    try expect(g2 == 1234);
-}
-
-test "comptime keyword on expressions" {
-    const x: i32 = comptime x: {
-        break :x 1 + 2 + 3;
-    };
-    try expect(x == comptime 6);
-}
-
-test "type equality" {
-    try expect(*const u8 != *u8);
-}
-
-test "pointer dereferencing" {
-    var x = @as(i32, 3);
-    const y = &x;
-
-    y.* += 1;
-
-    try expect(x == 4);
-    try expect(y.* == 4);
-}
-
-test "const expression eval handling of variables" {
-    var x = true;
-    while (x) {
-        x = false;
-    }
-}
-
-test "character literals" {
-    try expect('\'' == single_quote);
-}
-const single_quote = '\'';
-
-test "non const ptr to aliased type" {
-    const int = i32;
-    try expect(?*int == ?*i32);
-}
-
-test "cold function" {
-    thisIsAColdFn();
-    comptime thisIsAColdFn();
-}
-
-fn thisIsAColdFn() void {
-    @setCold(true);
-}
-
-test "unicode escape in character literal" {
-    var a: u24 = '\u{01f4a9}';
-    try expect(a == 128169);
-}
-
-test "unicode character in character literal" {
-    try expect('💩' == 128169);
-}
-
-fn first4KeysOfHomeRow() []const u8 {
-    return "aoeu";
-}
-
-test "return string from function" {
-    try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
-}
-
-test "hex escape" {
-    try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello"));
-}
-
-test "multiline string" {
-    const s1 =
-        \\one
-        \\two)
-        \\three
-    ;
-    const s2 = "one\ntwo)\nthree";
-    try expect(mem.eql(u8, s1, s2));
-}
-
-test "multiline string comments at start" {
-    const s1 =
-        //\\one
-        \\two)
-        \\three
-    ;
-    const s2 = "two)\nthree";
-    try expect(mem.eql(u8, s1, s2));
-}
-
-test "multiline string comments at end" {
-    const s1 =
-        \\one
-        \\two)
-        //\\three
-    ;
-    const s2 = "one\ntwo)";
-    try expect(mem.eql(u8, s1, s2));
-}
-
-test "multiline string comments in middle" {
-    const s1 =
-        \\one
-        //\\two)
-        \\three
-    ;
-    const s2 = "one\nthree";
-    try expect(mem.eql(u8, s1, s2));
-}
-
-test "multiline string comments at multiple places" {
-    const s1 =
-        \\one
-        //\\two
-        \\three
-        //\\four
-        \\five
-    ;
-    const s2 = "one\nthree\nfive";
-    try expect(mem.eql(u8, s1, s2));
-}
-
-test "call result of if else expression" {
-    try expect(mem.eql(u8, f2(true), "a"));
-    try expect(mem.eql(u8, f2(false), "b"));
-}
-fn f2(x: bool) []const u8 {
-    return (if (x) fA else fB)();
-}
-fn fA() []const u8 {
-    return "a";
-}
-fn fB() []const u8 {
-    return "b";
-}
-
-test "string concatenation" {
-    try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
-}
-
-test "array mult operator" {
-    try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
-}
-
-test "memcpy and memset intrinsics" {
-    try testMemcpyMemset();
-    // TODO add comptime test coverage
-    //comptime try testMemcpyMemset();
-}
-
-fn testMemcpyMemset() !void {
-    var foo: [20]u8 = undefined;
-    var bar: [20]u8 = undefined;
-
-    @memset(&foo, 'A', foo.len);
-    @memcpy(&bar, &foo, bar.len);
-
-    try expect(bar[0] == 'A');
-    try expect(bar[11] == 'A');
-    try expect(bar[19] == 'A');
-}
-
-const OpaqueA = opaque {};
-const OpaqueB = opaque {};
-
-test "opaque types" {
-    try expect(*OpaqueA != *OpaqueB);
-    if (!builtin.zig_is_stage2) {
-        try expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA"));
-        try expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB"));
-    }
-}
-
-test "variable is allowed to be a pointer to an opaque type" {
-    var x: i32 = 1234;
-    _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x));
-}
-fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA {
-    var a = ptr;
-    return a;
-}
-
-const global_a: i32 = 1234;
-const global_b: *const i32 = &global_a;
-const global_c: *const f32 = @ptrCast(*const f32, global_b);
-test "compile time global reinterpret" {
-    const d = @ptrCast(*const i32, global_c);
-    try expect(d.* == 1234);
-}
-
-test "cast undefined" {
-    const array: [100]u8 = undefined;
-    const slice = @as([]const u8, &array);
-    testCastUndefined(slice);
-}
-fn testCastUndefined(x: []const u8) void {
-    _ = x;
-}
-
-test "implicit cast after unreachable" {
-    try expect(outer() == 1234);
-}
-fn inner() i32 {
-    return 1234;
-}
-fn outer() i64 {
-    return inner();
-}
-
-test "take address of parameter" {
-    try testTakeAddressOfParameter(12.34);
-}
-fn testTakeAddressOfParameter(f: f32) !void {
-    const f_ptr = &f;
-    try expect(f_ptr.* == 12.34);
-}
-
-test "pointer to void return type" {
-    try testPointerToVoidReturnType();
-}
-fn testPointerToVoidReturnType() anyerror!void {
-    const a = testPointerToVoidReturnType2();
-    return a.*;
-}
-const test_pointer_to_void_return_type_x = void{};
-fn testPointerToVoidReturnType2() *const void {
-    return &test_pointer_to_void_return_type_x;
-}
-
-test "array 2D const double ptr" {
-    const rect_2d_vertexes = [_][1]f32{
-        [_]f32{1.0},
-        [_]f32{2.0},
-    };
-    try testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]);
-}
-
-fn testArray2DConstDoublePtr(ptr: *const f32) !void {
-    const ptr2 = @ptrCast([*]const f32, ptr);
-    try expect(ptr2[0] == 1.0);
-    try expect(ptr2[1] == 2.0);
-}
-
-test "double implicit cast in same expression" {
-    var x = @as(i32, @as(u16, nine()));
-    try expect(x == 9);
-}
-fn nine() u8 {
-    return 9;
-}
-
-test "comptime if inside runtime while which unconditionally breaks" {
-    testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
-    comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
-}
-fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void {
-    while (cond) {
-        if (false) {}
-        break;
-    }
-}
-
-test "implicit comptime while" {
-    while (false) {
-        @compileError("bad");
-    }
-}
-
-fn fnThatClosesOverLocalConst() type {
-    const c = 1;
-    return struct {
-        fn g() i32 {
-            return c;
-        }
-    };
-}
-
-test "function closes over local const" {
-    const x = fnThatClosesOverLocalConst().g();
-    try expect(x == 1);
-}
-
-test "volatile load and store" {
-    var number: i32 = 1234;
-    const ptr = @as(*volatile i32, &number);
-    ptr.* += 1;
-    try expect(ptr.* == 1235);
-}
-
-test "struct inside function" {
-    try testStructInFn();
-    comptime try testStructInFn();
-}
-
-fn testStructInFn() !void {
-    const BlockKind = u32;
-
-    const Block = struct {
-        kind: BlockKind,
-    };
-
-    var block = Block{ .kind = 1234 };
-
-    block.kind += 1;
-
-    try expect(block.kind == 1235);
-}
-
-test "fn call returning scalar optional in equality expression" {
-    try expect(getNull() == null);
-}
-
-fn getNull() ?*i32 {
-    return null;
-}
-
-var global_foo: *i32 = undefined;
-
-test "global variable assignment with optional unwrapping with var initialized to undefined" {
-    const S = struct {
-        var data: i32 = 1234;
-        fn foo() ?*i32 {
-            return &data;
-        }
-    };
-    global_foo = S.foo() orelse {
-        @panic("bad");
-    };
-    try expect(global_foo.* == 1234);
-}
-
-test "peer result location with typed parent, runtime condition, comptime prongs" {
-    const S = struct {
-        fn doTheTest(arg: i32) i32 {
-            const st = Structy{
-                .bleh = if (arg == 1) 1 else 1,
-            };
-
-            if (st.bleh == 1)
-                return 1234;
-            return 0;
-        }
-
-        const Structy = struct {
-            bleh: i32,
-        };
-    };
-    try expect(S.doTheTest(0) == 1234);
-    try expect(S.doTheTest(1) == 1234);
-}
-
-fn ZA() type {
-    return struct {
-        b: B(),
-
-        const Self = @This();
-
-        fn B() type {
-            return struct {
-                const Self = @This();
-            };
-        }
-    };
-}
-test "non-ambiguous reference of shadowed decls" {
-    try expect(ZA().B().Self != ZA().Self);
-}
-
-test "use of declaration with same name as primitive" {
-    const S = struct {
-        const @"u8" = u16;
-        const alias = @"u8";
-    };
-    const a: S.u8 = 300;
-    try expect(a == 300);
-
-    const b: S.alias = 300;
-    try expect(b == 300);
-
-    const @"u8" = u16;
-    const c: @"u8" = 300;
-    try expect(c == 300);
-}
-
-fn emptyFn() void {}
-
-test "constant equal function pointers" {
-    const alias = emptyFn;
-    try expect(comptime x: {
-        break :x emptyFn == alias;
-    });
-}
-
-test "multiline string literal is null terminated" {
-    const s1 =
-        \\one
-        \\two)
-        \\three
-    ;
-    const s2 = "one\ntwo)\nthree";
-    try expect(std.cstr.cmp(s1, s2) == 0);
-}
-
-test "self reference through fn ptr field" {
-    const S = struct {
-        const A = struct {
-            f: fn (A) u8,
-        };
-
-        fn foo(a: A) u8 {
-            _ = a;
-            return 12;
-        }
-    };
-    var a: S.A = undefined;
-    a.f = S.foo;
-    try expect(a.f(a) == 12);
-}
-
-test "global variable initialized to global variable array element" {
-    try expect(global_ptr == &gdt[0]);
-}
-const GDTEntry = struct {
-    field: i32,
-};
-var gdt = [_]GDTEntry{
-    GDTEntry{ .field = 1 },
-    GDTEntry{ .field = 2 },
-};
-var global_ptr = &gdt[0];
-
-test "global constant is loaded with a runtime-known index" {
-    const S = struct {
-        fn doTheTest() !void {
-            var index: usize = 1;
-            const ptr = &pieces[index].field;
-            try expect(ptr.* == 2);
-        }
-        const Piece = struct {
-            field: i32,
-        };
-        const pieces = [_]Piece{ Piece{ .field = 1 }, Piece{ .field = 2 }, Piece{ .field = 3 } };
-    };
-    try S.doTheTest();
-}
test/behavior/basic_llvm.zig
@@ -0,0 +1,458 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const mem = std.mem;
+const expect = std.testing.expect;
+const expectEqualStrings = std.testing.expectEqualStrings;
+
+const g1: i32 = 1233 + 1;
+var g2: i32 = 0;
+
+test "global variables" {
+    try expect(g2 == 0);
+    g2 = g1;
+    try expect(g2 == 1234);
+}
+
+test "comptime keyword on expressions" {
+    const x: i32 = comptime x: {
+        break :x 1 + 2 + 3;
+    };
+    try expect(x == comptime 6);
+}
+
+test "type equality" {
+    try expect(*const u8 != *u8);
+}
+
+test "pointer dereferencing" {
+    var x = @as(i32, 3);
+    const y = &x;
+
+    y.* += 1;
+
+    try expect(x == 4);
+    try expect(y.* == 4);
+}
+
+test "const expression eval handling of variables" {
+    var x = true;
+    while (x) {
+        x = false;
+    }
+}
+
+test "character literals" {
+    try expect('\'' == single_quote);
+}
+const single_quote = '\'';
+
+test "non const ptr to aliased type" {
+    const int = i32;
+    try expect(?*int == ?*i32);
+}
+
+test "cold function" {
+    thisIsAColdFn();
+    comptime thisIsAColdFn();
+}
+
+fn thisIsAColdFn() void {
+    @setCold(true);
+}
+
+test "unicode escape in character literal" {
+    var a: u24 = '\u{01f4a9}';
+    try expect(a == 128169);
+}
+
+test "unicode character in character literal" {
+    try expect('💩' == 128169);
+}
+
+fn first4KeysOfHomeRow() []const u8 {
+    return "aoeu";
+}
+
+test "return string from function" {
+    try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
+}
+
+test "hex escape" {
+    try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello"));
+}
+
+test "multiline string" {
+    const s1 =
+        \\one
+        \\two)
+        \\three
+    ;
+    const s2 = "one\ntwo)\nthree";
+    try expect(mem.eql(u8, s1, s2));
+}
+
+test "multiline string comments at start" {
+    const s1 =
+        //\\one
+        \\two)
+        \\three
+    ;
+    const s2 = "two)\nthree";
+    try expect(mem.eql(u8, s1, s2));
+}
+
+test "multiline string comments at end" {
+    const s1 =
+        \\one
+        \\two)
+        //\\three
+    ;
+    const s2 = "one\ntwo)";
+    try expect(mem.eql(u8, s1, s2));
+}
+
+test "multiline string comments in middle" {
+    const s1 =
+        \\one
+        //\\two)
+        \\three
+    ;
+    const s2 = "one\nthree";
+    try expect(mem.eql(u8, s1, s2));
+}
+
+test "multiline string comments at multiple places" {
+    const s1 =
+        \\one
+        //\\two
+        \\three
+        //\\four
+        \\five
+    ;
+    const s2 = "one\nthree\nfive";
+    try expect(mem.eql(u8, s1, s2));
+}
+
+test "call result of if else expression" {
+    try expect(mem.eql(u8, f2(true), "a"));
+    try expect(mem.eql(u8, f2(false), "b"));
+}
+fn f2(x: bool) []const u8 {
+    return (if (x) fA else fB)();
+}
+fn fA() []const u8 {
+    return "a";
+}
+fn fB() []const u8 {
+    return "b";
+}
+
+test "string concatenation" {
+    try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
+}
+
+test "array mult operator" {
+    try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
+}
+
+test "memcpy and memset intrinsics" {
+    try testMemcpyMemset();
+    // TODO add comptime test coverage
+    //comptime try testMemcpyMemset();
+}
+
+fn testMemcpyMemset() !void {
+    var foo: [20]u8 = undefined;
+    var bar: [20]u8 = undefined;
+
+    @memset(&foo, 'A', foo.len);
+    @memcpy(&bar, &foo, bar.len);
+
+    try expect(bar[0] == 'A');
+    try expect(bar[11] == 'A');
+    try expect(bar[19] == 'A');
+}
+
+const OpaqueA = opaque {};
+const OpaqueB = opaque {};
+
+test "opaque types" {
+    try expect(*OpaqueA != *OpaqueB);
+    if (!builtin.zig_is_stage2) {
+        try expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA"));
+        try expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB"));
+    }
+}
+
+test "variable is allowed to be a pointer to an opaque type" {
+    var x: i32 = 1234;
+    _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x));
+}
+fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA {
+    var a = ptr;
+    return a;
+}
+
+const global_a: i32 = 1234;
+const global_b: *const i32 = &global_a;
+const global_c: *const f32 = @ptrCast(*const f32, global_b);
+test "compile time global reinterpret" {
+    const d = @ptrCast(*const i32, global_c);
+    try expect(d.* == 1234);
+}
+
+test "cast undefined" {
+    const array: [100]u8 = undefined;
+    const slice = @as([]const u8, &array);
+    testCastUndefined(slice);
+}
+fn testCastUndefined(x: []const u8) void {
+    _ = x;
+}
+
+test "implicit cast after unreachable" {
+    try expect(outer() == 1234);
+}
+fn inner() i32 {
+    return 1234;
+}
+fn outer() i64 {
+    return inner();
+}
+
+test "take address of parameter" {
+    try testTakeAddressOfParameter(12.34);
+}
+fn testTakeAddressOfParameter(f: f32) !void {
+    const f_ptr = &f;
+    try expect(f_ptr.* == 12.34);
+}
+
+test "pointer to void return type" {
+    try testPointerToVoidReturnType();
+}
+fn testPointerToVoidReturnType() anyerror!void {
+    const a = testPointerToVoidReturnType2();
+    return a.*;
+}
+const test_pointer_to_void_return_type_x = void{};
+fn testPointerToVoidReturnType2() *const void {
+    return &test_pointer_to_void_return_type_x;
+}
+
+test "array 2D const double ptr" {
+    const rect_2d_vertexes = [_][1]f32{
+        [_]f32{1.0},
+        [_]f32{2.0},
+    };
+    try testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]);
+}
+
+fn testArray2DConstDoublePtr(ptr: *const f32) !void {
+    const ptr2 = @ptrCast([*]const f32, ptr);
+    try expect(ptr2[0] == 1.0);
+    try expect(ptr2[1] == 2.0);
+}
+
+test "double implicit cast in same expression" {
+    var x = @as(i32, @as(u16, nine()));
+    try expect(x == 9);
+}
+fn nine() u8 {
+    return 9;
+}
+
+test "comptime if inside runtime while which unconditionally breaks" {
+    testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
+    comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true);
+}
+fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void {
+    while (cond) {
+        if (false) {}
+        break;
+    }
+}
+
+test "implicit comptime while" {
+    while (false) {
+        @compileError("bad");
+    }
+}
+
+fn fnThatClosesOverLocalConst() type {
+    const c = 1;
+    return struct {
+        fn g() i32 {
+            return c;
+        }
+    };
+}
+
+test "function closes over local const" {
+    const x = fnThatClosesOverLocalConst().g();
+    try expect(x == 1);
+}
+
+test "volatile load and store" {
+    var number: i32 = 1234;
+    const ptr = @as(*volatile i32, &number);
+    ptr.* += 1;
+    try expect(ptr.* == 1235);
+}
+
+test "struct inside function" {
+    try testStructInFn();
+    comptime try testStructInFn();
+}
+
+fn testStructInFn() !void {
+    const BlockKind = u32;
+
+    const Block = struct {
+        kind: BlockKind,
+    };
+
+    var block = Block{ .kind = 1234 };
+
+    block.kind += 1;
+
+    try expect(block.kind == 1235);
+}
+
+test "fn call returning scalar optional in equality expression" {
+    try expect(getNull() == null);
+}
+
+fn getNull() ?*i32 {
+    return null;
+}
+
+var global_foo: *i32 = undefined;
+
+test "global variable assignment with optional unwrapping with var initialized to undefined" {
+    const S = struct {
+        var data: i32 = 1234;
+        fn foo() ?*i32 {
+            return &data;
+        }
+    };
+    global_foo = S.foo() orelse {
+        @panic("bad");
+    };
+    try expect(global_foo.* == 1234);
+}
+
+test "peer result location with typed parent, runtime condition, comptime prongs" {
+    const S = struct {
+        fn doTheTest(arg: i32) i32 {
+            const st = Structy{
+                .bleh = if (arg == 1) 1 else 1,
+            };
+
+            if (st.bleh == 1)
+                return 1234;
+            return 0;
+        }
+
+        const Structy = struct {
+            bleh: i32,
+        };
+    };
+    try expect(S.doTheTest(0) == 1234);
+    try expect(S.doTheTest(1) == 1234);
+}
+
+fn ZA() type {
+    return struct {
+        b: B(),
+
+        const Self = @This();
+
+        fn B() type {
+            return struct {
+                const Self = @This();
+            };
+        }
+    };
+}
+test "non-ambiguous reference of shadowed decls" {
+    try expect(ZA().B().Self != ZA().Self);
+}
+
+test "use of declaration with same name as primitive" {
+    const S = struct {
+        const @"u8" = u16;
+        const alias = @"u8";
+    };
+    const a: S.u8 = 300;
+    try expect(a == 300);
+
+    const b: S.alias = 300;
+    try expect(b == 300);
+
+    const @"u8" = u16;
+    const c: @"u8" = 300;
+    try expect(c == 300);
+}
+
+fn emptyFn() void {}
+
+test "constant equal function pointers" {
+    const alias = emptyFn;
+    try expect(comptime x: {
+        break :x emptyFn == alias;
+    });
+}
+
+test "multiline string literal is null terminated" {
+    const s1 =
+        \\one
+        \\two)
+        \\three
+    ;
+    const s2 = "one\ntwo)\nthree";
+    try expect(std.cstr.cmp(s1, s2) == 0);
+}
+
+test "self reference through fn ptr field" {
+    const S = struct {
+        const A = struct {
+            f: fn (A) u8,
+        };
+
+        fn foo(a: A) u8 {
+            _ = a;
+            return 12;
+        }
+    };
+    var a: S.A = undefined;
+    a.f = S.foo;
+    try expect(a.f(a) == 12);
+}
+
+test "global variable initialized to global variable array element" {
+    try expect(global_ptr == &gdt[0]);
+}
+const GDTEntry = struct {
+    field: i32,
+};
+var gdt = [_]GDTEntry{
+    GDTEntry{ .field = 1 },
+    GDTEntry{ .field = 2 },
+};
+var global_ptr = &gdt[0];
+
+test "global constant is loaded with a runtime-known index" {
+    const S = struct {
+        fn doTheTest() !void {
+            var index: usize = 1;
+            const ptr = &pieces[index].field;
+            try expect(ptr.* == 2);
+        }
+        const Piece = struct {
+            field: i32,
+        };
+        const pieces = [_]Piece{ Piece{ .field = 1 }, Piece{ .field = 2 }, Piece{ .field = 3 } };
+    };
+    try S.doTheTest();
+}
test/behavior.zig
@@ -2,6 +2,7 @@ const builtin = @import("builtin");
 
 test {
     // Tests that pass for stage1, stage2, and the C backend.
+    _ = @import("behavior/basic.zig");
     _ = @import("behavior/bool.zig");
     _ = @import("behavior/if.zig");
 
@@ -10,7 +11,7 @@ test {
         _ = @import("behavior/align.zig");
         _ = @import("behavior/array.zig");
         _ = @import("behavior/atomics.zig");
-        _ = @import("behavior/basic.zig");
+        _ = @import("behavior/basic_llvm.zig");
         _ = @import("behavior/bitcast.zig");
         _ = @import("behavior/bugs/394.zig");
         _ = @import("behavior/bugs/624.zig");