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(...@@ -1127,6 +1127,44 @@ fn transOffsetOfExpr(
1127 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO: implement complex OffsetOfExpr translation", .{});1127 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO: implement complex OffsetOfExpr translation", .{});
1128}1128}
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
1130fn transBinaryOperator(1168fn transBinaryOperator(
1131 c: *Context,1169 c: *Context,
1132 scope: *Scope,1170 scope: *Scope,
...@@ -1184,6 +1222,12 @@ fn transBinaryOperator(...@@ -1184,6 +1222,12 @@ fn transBinaryOperator(
1184 .LOr => {1222 .LOr => {
1185 return transCreateNodeBoolInfixOp(c, scope, stmt, .@"or", result_used);1223 return transCreateNodeBoolInfixOp(c, scope, stmt, .@"or", result_used);
1186 },1224 },
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 },
1187 else => {},1231 else => {},
1188 }1232 }
1189 var op_id: Tag = undefined;1233 var op_id: Tag = undefined;
test/run_translated_c.zig+26
...@@ -1131,4 +1131,30 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -1131,4 +1131,30 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1131 \\ return 0;1131 \\ return 0;
1132 \\}1132 \\}
1133 , "");1133 , "");
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 , "");
1134}1160}