translate-c: intcast compound assignment operand if different-sized integer

Use transCCast to cast the RHS of compound assignment if necessary.
This commit is contained in:
Evan Haas 2021-03-22 20:28:30 -07:00 committed by Veikka Tuominen
parent c8d721aa42
commit ab9324e604
3 changed files with 76 additions and 40 deletions

View File

@ -3197,43 +3197,34 @@ fn transCreateCompoundAssign(
const requires_int_cast = blk: {
const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt);
break :blk are_integers and !are_same_sign;
break :blk are_integers and !(are_same_sign and cIntTypeCmp(lhs_qt, rhs_qt) == .eq);
};
if (used == .unused) {
// common case
// c: lhs += rhs
// zig: lhs += rhs
const lhs_node = try transExpr(c, scope, lhs, .used);
var rhs_node = try transExpr(c, scope, rhs, .used);
if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
if ((is_mod or is_div) and is_signed) {
const lhs_node = try transExpr(c, scope, lhs, .used);
const rhs_node = try transExpr(c, scope, rhs, .used);
if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
const operands = .{ .lhs = lhs_node, .rhs = rhs_node };
const builtin = if (is_mod)
try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node })
try Tag.rem.create(c.arena, operands)
else
try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node });
try Tag.div_trunc.create(c.arena, operands);
return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used);
}
const lhs_node = try transExpr(c, scope, lhs, .used);
var rhs_node = if (is_shift or requires_int_cast)
try transExprCoercing(c, scope, rhs, .used)
else
try transExpr(c, scope, rhs, .used);
if (is_ptr_op_signed) {
rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
}
if (is_shift or requires_int_cast) {
// @intCast(rhs)
const cast_to_type = if (is_shift)
try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
else
try transQualType(c, scope, getExprQualType(c, lhs), loc);
if (is_shift) {
const cast_to_type = try qualTypeToLog2IntRef(c, scope, rhs_qt, loc);
rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
} else if (requires_int_cast) {
rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
}
return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used);
}
// worst case
@ -3255,29 +3246,24 @@ fn transCreateCompoundAssign(
const lhs_node = try Tag.identifier.create(c.arena, ref);
const ref_node = try Tag.deref.create(c.arena, lhs_node);
var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
if ((is_mod or is_div) and is_signed) {
const rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
const operands = .{ .lhs = ref_node, .rhs = rhs_node };
const builtin = if (is_mod)
try Tag.rem.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node })
try Tag.rem.create(c.arena, operands)
else
try Tag.div_trunc.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node });
try Tag.div_trunc.create(c.arena, operands);
const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used);
try block_scope.statements.append(assign);
} else {
var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
if (is_shift or requires_int_cast) {
// @intCast(rhs)
const cast_to_type = if (is_shift)
try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
else
try transQualType(c, scope, getExprQualType(c, lhs), loc);
if (is_shift) {
const cast_to_type = try qualTypeToLog2IntRef(c, &block_scope.base, rhs_qt, loc);
rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
}
if (is_ptr_op_signed) {
rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
} else if (requires_int_cast) {
rhs_node = try transCCast(c, &block_scope.base, loc, lhs_qt, rhs_qt, rhs_node);
}
const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used);

View File

@ -1258,4 +1258,54 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
\\ return 0;
\\}
, "");
cases.add("cast RHS of compound assignment if necessary, unused result",
\\#include <stdlib.h>
\\int main(void) {
\\ signed short val = -1;
\\ val += 1; if (val != 0) abort();
\\ val -= 1; if (val != -1) abort();
\\ val *= 2; if (val != -2) abort();
\\ val /= 2; if (val != -1) abort();
\\ val %= 2; if (val != -1) abort();
\\ val <<= 1; if (val != -2) abort();
\\ val >>= 1; if (val != -1) abort();
\\ val += 100000000; // compile error if @truncate() not inserted
\\ unsigned short uval = 1;
\\ uval += 1; if (uval != 2) abort();
\\ uval -= 1; if (uval != 1) abort();
\\ uval *= 2; if (uval != 2) abort();
\\ uval /= 2; if (uval != 1) abort();
\\ uval %= 2; if (uval != 1) abort();
\\ uval <<= 1; if (uval != 2) abort();
\\ uval >>= 1; if (uval != 1) abort();
\\ uval += 100000000; // compile error if @truncate() not inserted
\\}
, "");
cases.add("cast RHS of compound assignment if necessary, used result",
\\#include <stdlib.h>
\\int main(void) {
\\ signed short foo;
\\ signed short val = -1;
\\ foo = (val += 1); if (foo != 0) abort();
\\ foo = (val -= 1); if (foo != -1) abort();
\\ foo = (val *= 2); if (foo != -2) abort();
\\ foo = (val /= 2); if (foo != -1) abort();
\\ foo = (val %= 2); if (foo != -1) abort();
\\ foo = (val <<= 1); if (foo != -2) abort();
\\ foo = (val >>= 1); if (foo != -1) abort();
\\ foo = (val += 100000000); // compile error if @truncate() not inserted
\\ unsigned short ufoo;
\\ unsigned short uval = 1;
\\ ufoo = (uval += 1); if (ufoo != 2) abort();
\\ ufoo = (uval -= 1); if (ufoo != 1) abort();
\\ ufoo = (uval *= 2); if (ufoo != 2) abort();
\\ ufoo = (uval /= 2); if (ufoo != 1) abort();
\\ ufoo = (uval %= 2); if (ufoo != 1) abort();
\\ ufoo = (uval <<= 1); if (ufoo != 2) abort();
\\ ufoo = (uval >>= 1); if (ufoo != 1) abort();
\\ ufoo = (uval += 100000000); // compile error if @truncate() not inserted
\\}
, "");
}

View File

@ -2766,7 +2766,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ var a = arg_a;
\\ var i: c_int = 0;
\\ while (a > @bitCast(c_uint, @as(c_int, 0))) {
\\ a >>= @intCast(@import("std").math.Log2Int(c_int), 1);
\\ a >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
\\ }
\\ return i;
\\}
@ -2786,7 +2786,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ var a = arg_a;
\\ var i: c_int = 0;
\\ while (a > @bitCast(c_uint, @as(c_int, 0))) {
\\ a >>= @intCast(@import("std").math.Log2Int(c_int), 1);
\\ a >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
\\ }
\\ return i;
\\}