Commit 391ee996a5

Jadon Fowler <j@jadon.io>
2020-04-04 08:16:30
translate-c: account for signedness when translating div & mod
Signed-off-by: Jadon Fowler <j@jadon.io>
1 parent b9cb1e0
Changed files (2)
src-self-hosted
test
src-self-hosted/translate_c.zig
@@ -1170,7 +1170,7 @@ fn transBinaryOperator(
             }
         },
         .Div => {
-            if (!cIsUnsignedInteger(qt)) {
+            if (cIsSignedInteger(qt)) {
                 // signed integer division uses @divTrunc
                 const div_trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@divTrunc");
                 try div_trunc_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value));
@@ -1182,7 +1182,7 @@ fn transBinaryOperator(
             }
         },
         .Rem => {
-            if (!cIsUnsignedInteger(qt)) {
+            if (cIsSignedInteger(qt)) {
                 // signed integer division uses @rem
                 const rem_node = try transCreateNodeBuiltinFnCall(rp.c, "@rem");
                 try rem_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value));
@@ -3048,13 +3048,37 @@ fn transCreateCompoundAssign(
     used: ResultUsed,
 ) TransError!*ast.Node {
     const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight;
+    const is_div = bin_op == .Div;
+    const is_mod = bin_op == .Mod;
     const lhs = ZigClangCompoundAssignOperator_getLHS(stmt);
     const rhs = ZigClangCompoundAssignOperator_getRHS(stmt);
     const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt);
+    const is_signed = cIsSignedInteger(getExprQualType(rp.c, lhs));
     if (used == .unused) {
         // common case
         // c: lhs += rhs
         // zig: lhs += rhs
+
+        if ((is_mod or is_div) and is_signed) {
+            const op_token = try appendToken(rp.c, .Equal, "=");
+            const op_node = try rp.c.a().create(ast.Node.InfixOp);
+            const builtin = if (is_mod) "@rem" else "@divTrunc";
+            const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, builtin);
+            const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
+            try builtin_node.params.push(lhs_node);
+            _ = try appendToken(rp.c, .Comma, ",");
+            try builtin_node.params.push(try transExpr(rp, scope, rhs, .used, .r_value));
+            builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+            op_node.* = .{
+                .op_token = op_token,
+                .lhs = lhs_node,
+                .op = .Assign,
+                .rhs = &builtin_node.base,
+            };
+            _ = try appendToken(rp.c, .Semicolon, ";");
+            return &op_node.base;
+        }
+
         const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
         const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);
         var rhs_node = if (is_shift)
@@ -3097,31 +3121,53 @@ fn transCreateCompoundAssign(
     const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
     const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
     _ = try appendToken(rp.c, .Semicolon, ";");
-    const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);
-    var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
-    if (is_shift) {
-        const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast");
-        const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc);
-        try cast_node.params.push(rhs_type);
+
+    if ((is_mod or is_div) and is_signed) {
+        const op_token = try appendToken(rp.c, .Equal, "=");
+        const op_node = try rp.c.a().create(ast.Node.InfixOp);
+        const builtin = if (is_mod) "@rem" else "@divTrunc";
+        const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, builtin);
+        try builtin_node.params.push(try transCreateNodePtrDeref(rp.c, lhs_node));
         _ = try appendToken(rp.c, .Comma, ",");
-        try cast_node.params.push(rhs_node);
-        cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-        rhs_node = &cast_node.base;
-    }
-    const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);
+        try builtin_node.params.push(try transExpr(rp, scope, rhs, .used, .r_value));
+        builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+        _ = try appendToken(rp.c, .Semicolon, ";");
+        op_node.* = .{
+            .op_token = op_token,
+            .lhs = ref_node,
+            .op = .Assign,
+            .rhs = &builtin_node.base,
+        };
+        _ = try appendToken(rp.c, .Semicolon, ";");
+        try block_scope.block_node.statements.push(&op_node.base);
+    } else {
+        const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);
+        var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
 
-    _ = try appendToken(rp.c, .Semicolon, ";");
+        if (is_shift) {
+            const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast");
+            const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc);
+            try cast_node.params.push(rhs_type);
+            _ = try appendToken(rp.c, .Comma, ",");
+            try cast_node.params.push(rhs_node);
+            cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+            rhs_node = &cast_node.base;
+        }
 
-    const eq_token = try appendToken(rp.c, .Equal, "=");
-    const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, eq_token, rhs_bin, .used, false);
-    try block_scope.block_node.statements.push(assign);
+        const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);
+        _ = try appendToken(rp.c, .Semicolon, ";");
+
+        const eq_token = try appendToken(rp.c, .Equal, "=");
+        const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, eq_token, rhs_bin, .used, false);
+        try block_scope.block_node.statements.push(assign);
+    }
 
     const break_node = try transCreateNodeBreak(rp.c, block_scope.label);
     break_node.rhs = ref_node;
     try block_scope.block_node.statements.push(&break_node.base);
     block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}");
     // semicolon must immediately follow rbrace because it is the last token in a block
-    _ = try appendToken(rp.c, .Semicolon, ";");
+    _ = try appendToken(rp.c, .Semicolon, ";4");
     const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
     grouped_expr.* = .{
         .lparen = try appendToken(rp.c, .LParen, "("),
test/translate_c.zig
@@ -2375,20 +2375,24 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
     cases.add("compound assignment operators",
         \\void foo(void) {
         \\    int a = 0;
+        \\    unsigned b = 0;
         \\    a += (a += 1);
         \\    a -= (a -= 1);
         \\    a *= (a *= 1);
         \\    a &= (a &= 1);
         \\    a |= (a |= 1);
         \\    a ^= (a ^= 1);
-        \\    a /= (a /= 1);
-        \\    a %= (a %= 1);
         \\    a >>= (a >>= 1);
         \\    a <<= (a <<= 1);
+        \\    a /= (a /= 1);
+        \\    a %= (a %= 1);
+        \\    b /= (b /= 1);
+        \\    b %= (b %= 1);
         \\}
     , &[_][]const u8{
         \\pub export fn foo() void {
         \\    var a: c_int = 0;
+        \\    var b: c_uint = @bitCast(c_uint, @as(c_int, 0));
         \\    a += (blk: {
         \\        const ref = &a;
         \\        ref.* = ref.* + @as(c_int, 1);
@@ -2419,16 +2423,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\        ref.* = ref.* ^ @as(c_int, 1);
         \\        break :blk ref.*;
         \\    });
-        \\    a /= (blk: {
-        \\        const ref = &a;
-        \\        ref.* = ref.* / @as(c_int, 1);
-        \\        break :blk ref.*;
-        \\    });
-        \\    a %= (blk: {
-        \\        const ref = &a;
-        \\        ref.* = ref.* % @as(c_int, 1);
-        \\        break :blk ref.*;
-        \\    });
         \\    a >>= @intCast(@import("std").math.Log2Int(c_int), (blk: {
         \\        const ref = &a;
         \\        ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
@@ -2439,6 +2433,26 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\        ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
         \\        break :blk ref.*;
         \\    }));
+        \\    a = @divTrunc(a, (blk: {
+        \\        const ref = &a;
+        \\        ref.* = @divTrunc(ref.*, @as(c_int, 1));
+        \\        break :blk ref.*;
+        \\    }));
+        \\    a = @rem(a, (blk: {
+        \\        const ref = &a;
+        \\        ref.* = @rem(ref.*, @as(c_int, 1));
+        \\        break :blk ref.*;
+        \\    }));
+        \\    b /= (blk: {
+        \\        const ref = &b;
+        \\        ref.* = ref.* / @bitCast(c_uint, @as(c_int, 1));
+        \\        break :blk ref.*;
+        \\    });
+        \\    b %= (blk: {
+        \\        const ref = &b;
+        \\        ref.* = ref.* % @bitCast(c_uint, @as(c_int, 1));
+        \\        break :blk ref.*;
+        \\    });
         \\}
     });