authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-03-03 19:53:48-08:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-03-05 14:16:40+02:00
log291edafa1b3e6f56f88c3d1c542bdb25e99e45d1
tree943031717a44342403e63b6b957fd04332edbbd7
parent02737d535ac5bda4e9a00ec0ae11ae313065dfbb

translate-c: enable pointer arithmetic with signed integer operand

Given a pointer operand `ptr` and a signed integer operand `idx` `ptr + idx` and `idx + ptr` -> ptr + @bitCast(usize, @intCast(isize, idx)) `ptr - idx` -> ptr - @bitCast(usize, @intCast(isize, idx)) Thanks @LemonBoy for pointing out that we can take advantage of wraparound to dramatically simplify the code.

2 files changed, 70 insertions(+), 0 deletions(-)

src/translate_c.zig+44
......@@ -1127,6 +1127,44 @@ fn transOffsetOfExpr(
11271127 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO: implement complex OffsetOfExpr translation", .{});
11281128}
11291129
1130/// Translate an arithmetic expression with a pointer operand and a signed-integer operand.
1131/// Zig requires a usize argument for pointer arithmetic, so we intCast to isize and then
1132/// bitcast to usize; pointer wraparound make the math work.
1133/// Zig pointer addition is not commutative (unlike C); the pointer operand needs to be on the left.
1134/// The + operator in C is not a sequence point so it should be safe to switch the order if necessary.
1135fn transCreatePointerArithmeticSignedOp(
1136 c: *Context,
1137 scope: *Scope,
1138 stmt: *const clang.BinaryOperator,
1139 result_used: ResultUsed,
1140) TransError!Node {
1141 const is_add = stmt.getOpcode() == .Add;
1142 const lhs = stmt.getLHS();
1143 const rhs = stmt.getRHS();
1144 const swap_operands = is_add and cIsSignedInteger(getExprQualType(c, lhs));
1145
1146 const swizzled_lhs = if (swap_operands) rhs else lhs;
1147 const swizzled_rhs = if (swap_operands) lhs else rhs;
1148
1149 const lhs_node = try transExpr(c, scope, swizzled_lhs, .used);
1150 const rhs_node = try transExpr(c, scope, swizzled_rhs, .used);
1151
1152 const intcast_node = try Tag.int_cast.create(c.arena, .{
1153 .lhs = try Tag.identifier.create(c.arena, "isize"),
1154 .rhs = rhs_node,
1155 });
1156
1157 const bitcast_node = try Tag.bit_cast.create(c.arena, .{
1158 .lhs = try Tag.identifier.create(c.arena, "usize"),
1159 .rhs = intcast_node,
1160 });
1161
1162 const arith_args = .{ .lhs = lhs_node, .rhs = bitcast_node };
1163 const arith_node = try if (is_add) Tag.add.create(c.arena, arith_args) else Tag.sub.create(c.arena, arith_args);
1164
1165 return maybeSuppressResult(c, scope, result_used, arith_node);
1166}
1167
11301168fn transBinaryOperator(
11311169 c: *Context,
11321170 scope: *Scope,
......@@ -1184,6 +1222,12 @@ fn transBinaryOperator(
11841222 .LOr => {
11851223 return transCreateNodeBoolInfixOp(c, scope, stmt, .@"or", result_used);
11861224 },
1225 .Add, .Sub => {
1226 // `ptr + idx` and `idx + ptr` -> ptr + @bitCast(usize, @intCast(isize, idx))
1227 // `ptr - idx` -> ptr - @bitCast(usize, @intCast(isize, idx))
1228 if (qualTypeIsPtr(qt) and (cIsSignedInteger(getExprQualType(c, stmt.getLHS())) or
1229 cIsSignedInteger(getExprQualType(c, stmt.getRHS())))) return transCreatePointerArithmeticSignedOp(c, scope, stmt, result_used);
1230 },
11871231 else => {},
11881232 }
11891233 var op_id: Tag = undefined;
test/run_translated_c.zig+26
......@@ -1131,4 +1131,30 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
11311131 \\ return 0;
11321132 \\}
11331133 , "");
1134
1135 cases.add("pointer arithmetic with signed operand",
1136 \\#include <stdlib.h>
1137 \\int main() {
1138 \\ int array[10];
1139 \\ int *x = &array[5];
1140 \\ int *y;
1141 \\ int idx = 0;
1142 \\ y = x + ++idx;
1143 \\ if (y != x + 1 || y != &array[6]) abort();
1144 \\ y = idx + x;
1145 \\ if (y != x + 1 || y != &array[6]) abort();
1146 \\ y = x - idx;
1147 \\ if (y != x - 1 || y != &array[4]) abort();
1148 \\
1149 \\ idx = 0;
1150 \\ y = --idx + x;
1151 \\ if (y != x - 1 || y != &array[4]) abort();
1152 \\ y = idx + x;
1153 \\ if (y != x - 1 || y != &array[4]) abort();
1154 \\ y = x - idx;
1155 \\ if (y != x + 1 || y != &array[6]) abort();
1156 \\
1157 \\ return 0;
1158 \\}
1159 , "");
11341160}