translate-c: account for signedness when translating div & mod

Signed-off-by: Jadon Fowler <j@jadon.io>
This commit is contained in:
Jadon Fowler 2020-04-04 02:16:30 -04:00
parent b9cb1e0d83
commit 391ee996a5
2 changed files with 91 additions and 31 deletions

View File

@ -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;
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);
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 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 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, "("),

View File

@ -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.*;
\\ });
\\}
});