authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-02-22 13:09:51-08:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-02-25 22:24:11+02:00
log4f11a88b9f21a365cf2281e840ed12b0d4e687a6
tree2cdfb246283630b20a5ab1753175d4e724f42c3c
parent53cc63f0c91ce9f6f9c81f87e2673c5adfe1afe7

translate-c: Add support for pointer subtraction

When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. The size of the result is implementation-defined, and its type (a signed integer type) is ptrdiff_t defined in the <stddef.h> header. If the result is not representable in an object of that type, the behavior is undefined. See C Standard, §6.5.6 [ISO/IEC 9899:2011] Fixes #7216

3 files changed, 143 insertions(+), 2 deletions(-)

src/translate_c.zig+39-2
......@@ -1083,6 +1083,7 @@ fn transBinaryOperator(
10831083) TransError!Node {
10841084 const op = stmt.getOpcode();
10851085 const qt = stmt.getType();
1086 const isPointerDiffExpr = cIsPointerDiffExpr(c, stmt);
10861087 switch (op) {
10871088 .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()),
10881089 .Comma => {
......@@ -1143,7 +1144,7 @@ fn transBinaryOperator(
11431144 }
11441145 },
11451146 .Sub => {
1146 if (cIsUnsignedInteger(qt)) {
1147 if (cIsUnsignedInteger(qt) or isPointerDiffExpr) {
11471148 op_id = .sub_wrap;
11481149 } else {
11491150 op_id = .sub;
......@@ -1199,15 +1200,40 @@ fn transBinaryOperator(
11991200
12001201 const lhs = if (isBoolRes(lhs_uncasted))
12011202 try Tag.bool_to_int.create(c.arena, lhs_uncasted)
1203 else if (isPointerDiffExpr)
1204 try Tag.ptr_to_int.create(c.arena, lhs_uncasted)
12021205 else
12031206 lhs_uncasted;
12041207
12051208 const rhs = if (isBoolRes(rhs_uncasted))
12061209 try Tag.bool_to_int.create(c.arena, rhs_uncasted)
1210 else if (isPointerDiffExpr)
1211 try Tag.ptr_to_int.create(c.arena, rhs_uncasted)
12071212 else
12081213 rhs_uncasted;
12091214
1210 return transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, result_used);
1215 const infixOpNode = try transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, result_used);
1216 if (isPointerDiffExpr) {
1217 // @divExact(@bitCast(<platform-ptrdiff_t>, @ptrToInt(lhs) -% @ptrToInt(rhs)), @sizeOf(<lhs target type>))
1218 const ptrdiff_type = try transQualTypeIntWidthOf(c, qt, true);
1219
1220 // C standard requires that pointer subtraction operands are of the same type,
1221 // otherwise it is undefined behavior. So we can assume the left and right
1222 // sides are the same QualType and arbitrarily choose left.
1223 const lhs_expr = stmt.getLHS();
1224 const lhs_qt = getExprQualType(c, lhs_expr);
1225 const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc());
1226 const elem_type = lhs_qt_translated.castTag(.c_pointer).?.data.elem_type;
1227 const sizeof = try Tag.sizeof.create(c.arena, elem_type);
1228
1229 const bitcast = try Tag.bit_cast.create(c.arena, .{ .lhs = ptrdiff_type, .rhs = infixOpNode });
1230
1231 return Tag.div_exact.create(c.arena, .{
1232 .lhs = bitcast,
1233 .rhs = sizeof,
1234 });
1235 }
1236 return infixOpNode;
12111237}
12121238
12131239fn transCompoundStmtInline(
......@@ -1683,6 +1709,17 @@ fn transStringLiteralAsArray(
16831709 });
16841710}
16851711
1712/// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where
1713/// both operands resolve to addresses. The C standard requires that both operands
1714/// point to elements of the same array object, but we do not verify that here.
1715fn cIsPointerDiffExpr(c: *Context, stmt: *const clang.BinaryOperator) bool {
1716 const lhs = @ptrCast(*const clang.Stmt, stmt.getLHS());
1717 const rhs = @ptrCast(*const clang.Stmt, stmt.getRHS());
1718 return stmt.getOpcode() == .Sub and
1719 qualTypeIsPtr(@ptrCast(*const clang.Expr, lhs).getType()) and
1720 qualTypeIsPtr(@ptrCast(*const clang.Expr, rhs).getType());
1721}
1722
16861723fn cIsEnum(qt: clang.QualType) bool {
16871724 return qt.getCanonicalType().getTypeClass() == .Enum;
16881725}
src/translate_c/ast.zig+8
......@@ -146,6 +146,8 @@ pub const Node = extern union {
146146 align_cast,
147147 /// @ptrCast(lhs, rhs)
148148 ptr_cast,
149 /// @divExact(lhs, rhs)
150 div_exact,
149151
150152 negate,
151153 negate_wrap,
......@@ -300,6 +302,7 @@ pub const Node = extern union {
300302 .array_access,
301303 .std_mem_zeroinit,
302304 .ptr_cast,
305 .div_exact,
303306 => Payload.BinOp,
304307
305308 .integer_literal,
......@@ -1128,6 +1131,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
11281131 const payload = node.castTag(.ptr_cast).?.data;
11291132 return renderBuiltinCall(c, "@ptrCast", &.{ payload.lhs, payload.rhs });
11301133 },
1134 .div_exact => {
1135 const payload = node.castTag(.div_exact).?.data;
1136 return renderBuiltinCall(c, "@divExact", &.{ payload.lhs, payload.rhs });
1137 },
11311138 .sizeof => {
11321139 const payload = node.castTag(.sizeof).?.data;
11331140 return renderBuiltinCall(c, "@sizeOf", &.{payload});
......@@ -1993,6 +2000,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
19932000 .call,
19942001 .array_type,
19952002 .bool_to_int,
2003 .div_exact,
19962004 => {
19972005 // no grouping needed
19982006 return renderNode(c, node);
test/run_translated_c.zig+96
......@@ -958,4 +958,100 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
958958 \\ return 0;
959959 \\}
960960 , "");
961
962 cases.add("pointer difference: scalar array w/ size truncation or negative result. Issue #7216",
963 \\#include <stdlib.h>
964 \\#include <stddef.h>
965 \\#define SIZE 10
966 \\int main() {
967 \\ int foo[SIZE];
968 \\ int *start = &foo[0];
969 \\ int *one_past_end = start + SIZE;
970 \\ ptrdiff_t diff = one_past_end - start;
971 \\ char diff_char = one_past_end - start;
972 \\ if (diff != SIZE || diff_char != SIZE) abort();
973 \\ diff = start - one_past_end;
974 \\ if (diff != -SIZE) abort();
975 \\ if (one_past_end - foo != SIZE) abort();
976 \\ if ((one_past_end - 1) - foo != SIZE - 1) abort();
977 \\ if ((start + 1) - foo != 1) abort();
978 \\ return 0;
979 \\}
980 , "");
981
982 // C standard: if the expression P points either to an element of an array object or one
983 // past the last element of an array object, and the expression Q points to the last
984 // element of the same array object, the expression ((Q)+1)-(P) has the same value as
985 // ((Q)-(P))+1 and as -((P)-((Q)+1)), and has the value zero if the expression P points
986 // one past the last element of the array object, even though the expression (Q)+1
987 // does not point to an element of the array object
988 cases.add("pointer difference: C standard edge case",
989 \\#include <stdlib.h>
990 \\#include <stddef.h>
991 \\#define SIZE 10
992 \\int main() {
993 \\ int foo[SIZE];
994 \\ int *start = &foo[0];
995 \\ int *P = start + SIZE;
996 \\ int *Q = &foo[SIZE - 1];
997 \\ if ((Q + 1) - P != 0) abort();
998 \\ if ((Q + 1) - P != (Q - P) + 1) abort();
999 \\ if ((Q + 1) - P != -(P - (Q + 1))) abort();
1000 \\ return 0;
1001 \\}
1002 , "");
1003
1004 cases.add("pointer difference: unary operators",
1005 \\#include <stdlib.h>
1006 \\int main() {
1007 \\ int foo[10];
1008 \\ int *x = &foo[1];
1009 \\ const int *y = &foo[5];
1010 \\ if (y - x++ != 4) abort();
1011 \\ if (y - x != 3) abort();
1012 \\ if (y - ++x != 2) abort();
1013 \\ if (y - x-- != 2) abort();
1014 \\ if (y - x != 3) abort();
1015 \\ if (y - --x != 4) abort();
1016 \\ if (y - &foo[0] != 5) abort();
1017 \\ return 0;
1018 \\}
1019 , "");
1020
1021 cases.add("pointer difference: struct array with padding",
1022 \\#include <stdlib.h>
1023 \\#include <stddef.h>
1024 \\#define SIZE 10
1025 \\typedef struct my_struct {
1026 \\ int x;
1027 \\ char c;
1028 \\ int y;
1029 \\} my_struct_t;
1030 \\int main() {
1031 \\ my_struct_t foo[SIZE];
1032 \\ my_struct_t *start = &foo[0];
1033 \\ my_struct_t *one_past_end = start + SIZE;
1034 \\ ptrdiff_t diff = one_past_end - start;
1035 \\ int diff_int = one_past_end - start;
1036 \\ if (diff != SIZE || diff_int != SIZE) abort();
1037 \\ diff = start - one_past_end;
1038 \\ if (diff != -SIZE) abort();
1039 \\ return 0;
1040 \\}
1041 , "");
1042
1043 cases.add("pointer difference: array of function pointers",
1044 \\#include <stdlib.h>
1045 \\int a(void) { return 1;}
1046 \\int b(void) { return 2;}
1047 \\int c(void) { return 3;}
1048 \\typedef int (*myfunc)(void);
1049 \\int main() {
1050 \\ myfunc arr[] = {a, b, c, a, b, c};
1051 \\ myfunc *f1 = &arr[1];
1052 \\ myfunc *f4 = &arr[4];
1053 \\ if (f4 - f1 != 3) abort();
1054 \\ return 0;
1055 \\}
1056 , "");
9611057}