Commit 106520329e

Veikka Tuominen <git@vexu.eu>
2021-01-27 10:40:34
stage2 cbe: implement switchbr
1 parent 258f3ec
Changed files (4)
src
test
stage2
src/codegen/c.zig
@@ -129,6 +129,9 @@ pub const DeclGen = struct {
         t: Type,
         val: Value,
     ) error{ OutOfMemory, AnalysisFail }!void {
+        if (val.isUndef()) {
+            return dg.fail(dg.decl.src(), "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{});
+        }
         switch (t.zigTypeTag()) {
             .Int => {
                 if (t.isSignedInt())
@@ -196,6 +199,7 @@ pub const DeclGen = struct {
                     },
                 }
             },
+            .Bool => return writer.print("{}", .{val.toBool()}),
             else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{
                 @tagName(e),
             }),
@@ -409,6 +413,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
             .condbr => try genCondBr(o, inst.castTag(.condbr).?),
             .br => try genBr(o, inst.castTag(.br).?),
             .brvoid => try genBrVoid(o, inst.castTag(.brvoid).?.block),
+            .switchbr => try genSwitchBr(o, inst.castTag(.switchbr).?),
+            // booland and boolor are non-short-circuit operations
+            .booland => try genBinOp(o, inst.castTag(.booland).?, " & "),
+            .boolor => try genBinOp(o, inst.castTag(.boolor).?, " | "),
             else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
         };
         switch (result_value) {
@@ -688,6 +696,33 @@ fn genCondBr(o: *Object, inst: *Inst.CondBr) !CValue {
     return CValue.none;
 }
 
+fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue {
+    const target = try o.resolveInst(inst.target);
+    const writer = o.writer();
+
+    try writer.writeAll("switch (");
+    try o.writeCValue(writer, target);
+    try writer.writeAll(") {\n");
+    o.indent_writer.pushIndent();
+
+    for (inst.cases) |case| {
+        try writer.writeAll("case ");
+        try o.dg.renderValue(writer, inst.target.ty, case.item);
+        try writer.writeAll(": ");
+        // the case body must be noreturn so we don't need to insert a break
+        try genBody(o, case.body);
+        try o.indent_writer.insertNewline();
+    }
+
+    try writer.writeAll("default: ");
+    try genBody(o, inst.else_body);
+    try o.indent_writer.insertNewline();
+
+    o.indent_writer.popIndent();
+    try writer.writeAll("}\n");
+    return CValue.none;
+}
+
 fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
     if (as.base.isUnused() and !as.is_volatile)
         return CValue.none;
src/ir.zig
@@ -521,7 +521,7 @@ pub const Inst = struct {
         pub const base_tag = Tag.switchbr;
 
         base: Inst,
-        target_ptr: *Inst,
+        target: *Inst,
         cases: []Case,
         /// Set of instructions whose lifetimes end at the start of one of the cases.
         /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ].
@@ -544,7 +544,7 @@ pub const Inst = struct {
             var i = index;
 
             if (i < 1)
-                return self.target_ptr;
+                return self.target;
             i -= 1;
 
             return null;
src/Module.zig
@@ -2215,7 +2215,7 @@ pub fn addSwitchBr(
     self: *Module,
     block: *Scope.Block,
     src: usize,
-    target_ptr: *Inst,
+    target: *Inst,
     cases: []Inst.SwitchBr.Case,
     else_body: ir.Body,
 ) !*Inst {
@@ -2226,7 +2226,7 @@ pub fn addSwitchBr(
             .ty = Type.initTag(.noreturn),
             .src = src,
         },
-        .target_ptr = target_ptr,
+        .target = target,
         .cases = cases,
         .else_body = else_body,
     };
test/stage2/cbe.zig
@@ -133,45 +133,6 @@ pub fn addCases(ctx: *TestContext) !void {
             \\}
             \\
         , "");
-
-        // Simple while loop
-        case.addCompareOutput(
-            \\export fn main() c_int {
-            \\    var a: c_int = 0;
-            \\    while (a < 5) : (a+=1) {}
-            \\    exit(a - 5);
-            \\}
-            \\
-            \\fn exit(code: usize) noreturn {
-            \\    asm volatile ("syscall"
-            \\        :
-            \\        : [number] "{rax}" (231),
-            \\          [arg1] "{rdi}" (code)
-            \\    );
-            \\    unreachable;
-            \\}
-        , "");
-
-        // If expression
-        case.addCompareOutput(
-            \\export fn main() c_int {
-            \\    var cond: c_int = 0;
-            \\    var a: c_int = @as(c_int, if (cond == 0)
-            \\        2
-            \\    else
-            \\        3) + 9;
-            \\    exit(a - 11);
-            \\}
-            \\
-            \\fn exit(code: usize) noreturn {
-            \\    asm volatile ("syscall"
-            \\        :
-            \\        : [number] "{rax}" (231),
-            \\          [arg1] "{rdi}" (code)
-            \\    );
-            \\    unreachable;
-            \\}
-        , "");
     }
 
     {
@@ -224,6 +185,45 @@ pub fn addCases(ctx: *TestContext) !void {
             \\}
         , "");
     }
+    {
+        var case = ctx.exeFromCompiledC("control flow", .{});
+
+        // Simple while loop
+        case.addCompareOutput(
+            \\export fn main() c_int {
+            \\    var a: c_int = 0;
+            \\    while (a < 5) : (a+=1) {}
+            \\    return a - 5;
+            \\}
+        , "");
+
+        // If expression
+        case.addCompareOutput(
+            \\export fn main() c_int {
+            \\    var cond: c_int = 0;
+            \\    var a: c_int = @as(c_int, if (cond == 0)
+            \\        2
+            \\    else
+            \\        3) + 9;
+            \\    return a - 11;
+            \\}
+        , "");
+
+        // Switch expression
+        case.addCompareOutput(
+            \\export fn main() c_int {
+            \\    var cond: c_int = 0;
+            \\    var a: c_int = switch (cond) {
+            \\        1 => 1,
+            \\        2 => 2,
+            \\        99...300, 12 => 3,
+            \\        0 => 4,
+            \\        else => 5,
+            \\    };
+            \\    return a - 4;
+            \\}
+        , "");
+    }
     ctx.c("empty start function", linux_x64,
         \\export fn _start() noreturn {
         \\    unreachable;