Skip to content

Commit 4a95606

Browse files
committed
Bug 580201 - narrowing for constexpr evaluation of builtins
Implement narrowing of argument value (in constexpr context) for various builtins. Change-Id: I428cc789358638bf3796ea706f459032c8be10a1
1 parent b1011b0 commit 4a95606

File tree

5 files changed

+171
-36
lines changed

5 files changed

+171
-36
lines changed

core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ private static Map<String, String> getGnuMap() {
113113
map.put("__SIZEOF_SHORT__", "2");
114114
map.put("__SIZEOF_INT__", "4");
115115
map.put("__SIZEOF_LONG__", "8");
116+
map.put("__SIZEOF_LONG_LONG__", "8");
116117
map.put("__SIZEOF_DOUBLE__", "8");
117118
map.put("__SIZEOF_POINTER__", "8");
118119
return map;
@@ -123,6 +124,7 @@ private static Map<String, String> getStdMap() {
123124
map.put("__SIZEOF_SHORT__", "2");
124125
map.put("__SIZEOF_INT__", "4");
125126
map.put("__SIZEOF_LONG__", "8");
127+
map.put("__SIZEOF_LONG_LONG__", "8");
126128
map.put("__SIZEOF_DOUBLE__", "8");
127129
map.put("__SIZEOF_POINTER__", "8");
128130
return map;

core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/IntegralValueTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*******************************************************************************/
1212
package org.eclipse.cdt.core.parser.tests.ast2.cxx14.constexpr;
1313

14+
import org.eclipse.cdt.core.testplugin.TestScannerProvider;
1415
import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
1516

1617
import junit.framework.TestSuite;
@@ -36,6 +37,16 @@ public static TestSuite suite() {
3637
}
3738
}
3839

40+
@Override
41+
protected void setUp() throws Exception {
42+
TestScannerProvider.sDefinedSymbols.put("__SIZEOF_SHORT__", "2");
43+
TestScannerProvider.sDefinedSymbols.put("__SIZEOF_INT__", "4");
44+
TestScannerProvider.sDefinedSymbols.put("__SIZEOF_LONG__", "8");
45+
TestScannerProvider.sDefinedSymbols.put("__SIZEOF_LONG_LONG__", "8");
46+
TestScannerProvider.sDefinedSymbols.put("__SIZEOF_POINTER__", "8");
47+
super.setUp();
48+
}
49+
3950
// constexpr auto x = int{} + int();
4051
public void testIntDefaultValue() throws Exception {
4152
assertEvaluationEquals(0);
@@ -275,6 +286,16 @@ public void testBuiltinFfs() throws Exception {
275286
assertEvaluationEquals(5);
276287
}
277288

289+
// constexpr int x = __builtin_ffs(0x100000000);
290+
public void testBuiltinFfsNarrowing() throws Exception {
291+
assertEvaluationEquals(0);
292+
}
293+
294+
// constexpr int x = __builtin_ffsl(0x100000000);
295+
public void testBuiltinFfsl() throws Exception {
296+
assertEvaluationEquals(33);
297+
}
298+
278299
// constexpr int x = __builtin_ctz(16);
279300
public void testBuiltinCtz() throws Exception {
280301
assertEvaluationEquals(4);
@@ -314,4 +335,9 @@ public void testBuiltinAbs() throws Exception {
314335
public void testBuiltinAbsNegativeInput() throws Exception {
315336
assertEvaluationEquals(1);
316337
}
338+
339+
// constexpr int x = __builtin_abs(0xFFFFFFFF);
340+
public void testBuiltinAbsNarrowing() throws Exception {
341+
assertEvaluationEquals(1);
342+
}
317343
}

core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ private static Map<String, String> getStdMap() {
6666
map.put("__SIZEOF_SHORT__", "2");
6767
map.put("__SIZEOF_INT__", "4");
6868
map.put("__SIZEOF_LONG__", "8");
69+
map.put("__SIZEOF_LONG_LONG__", "8");
6970
map.put("__SIZEOF_POINTER__", "8");
7071
return map;
7172
}

core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/Conversions.java

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getNestedType;
3131
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.isVoidType;
3232

33+
import java.math.BigInteger;
3334
import java.util.Collections;
3435

3536
import org.eclipse.cdt.core.dom.ast.DOMException;
@@ -60,6 +61,8 @@
6061
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
6162
import org.eclipse.cdt.internal.core.dom.parser.ArithmeticConversion;
6263
import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer;
64+
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator;
65+
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator.SizeAndAlignment;
6366
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType;
6467
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerToMemberType;
6568
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType;
@@ -1326,4 +1329,99 @@ public static IType copyPointer(final IPointerType p1, IType target, final boole
13261329
private static boolean isNullPtr(IType t1) {
13271330
return t1 instanceof IBasicType && ((IBasicType) t1).getKind() == Kind.eNullPtr;
13281331
}
1332+
1333+
/**
1334+
* Narrow a numeric value to the range of a specified type.
1335+
* @param num the value to narrow (may be null)
1336+
* @param toType the type to narrow to
1337+
* @return a number representing the narrowed value, or null
1338+
*/
1339+
public static Number narrowNumberValue(Number num, IType toType) {
1340+
if (num == null)
1341+
return null;
1342+
1343+
if (toType instanceof IBasicType) {
1344+
IBasicType basicType = (IBasicType) toType;
1345+
IBasicType.Kind basicTypeKind = basicType.getKind();
1346+
switch (basicTypeKind) {
1347+
case eFloat:
1348+
if (num instanceof Float)
1349+
return num;
1350+
return Float.valueOf(num.floatValue());
1351+
case eDouble:
1352+
if (num instanceof Double)
1353+
return num;
1354+
return Double.valueOf(num.doubleValue());
1355+
case eInt:
1356+
SizeAndAlignment sizeToType = SizeofCalculator.getSizeAndAlignment(toType);
1357+
if (sizeToType == null)
1358+
return null;
1359+
// Note in the following we don't check type.isSigned() since that checks for the
1360+
// explicit presence of the "signed" modifier. So instead check !type.isUnsigned().
1361+
if (sizeToType.size <= 8) {
1362+
// First, mask the value to the correct size
1363+
// Note that we take the longValue here which may be negative even though the
1364+
// original value is positive; the masking here should still be correct and we
1365+
// should ultimately end up with the correct narrowed value, regardless.
1366+
long longVal = num.longValue();
1367+
long maskVal = 0xFFFFFFFFFFFFFFFFL;
1368+
long signBit = 0x8000000000000000L;
1369+
// Calculate a mask to reduce the size of the value to the target width:
1370+
maskVal >>>= (8 - sizeToType.size) * 8;
1371+
signBit >>>= (8 - sizeToType.size) * 8;
1372+
if (!basicType.isUnsigned() && (longVal & signBit) != 0) {
1373+
// We need to extend the sign bit.
1374+
long signBits = ~maskVal;
1375+
longVal |= signBits;
1376+
} else {
1377+
longVal &= maskVal;
1378+
}
1379+
1380+
// The Java type used to store the numerical value is independent of the associated
1381+
// C type, but we go with a smaller type (Integer) where possible. For 4 bytes
1382+
// (signed) or less than 4 bytes (signed or not) we can use Integer. For 8 bytes
1383+
// (signed) or less than 8 bytes (signed or not) we can use Long. Any larger and we
1384+
// resort to BigInteger.
1385+
if (longVal >= 0 && longVal <= Integer.MAX_VALUE) {
1386+
return Integer.valueOf((int) longVal);
1387+
}
1388+
if (!basicType.isUnsigned() && longVal >= Integer.MIN_VALUE && longVal <= Integer.MAX_VALUE) {
1389+
return Integer.valueOf((int) longVal);
1390+
}
1391+
1392+
if (!basicType.isUnsigned() || longVal > 0) {
1393+
return Long.valueOf(longVal);
1394+
}
1395+
1396+
BigInteger biVal = BigInteger.valueOf(longVal);
1397+
// 2**64 = 18446744073709551616
1398+
biVal = biVal.add(new BigInteger("18446744073709551616")); //$NON-NLS-1$
1399+
return biVal;
1400+
}
1401+
// TODO handle larger int sizes?
1402+
return null;
1403+
case eChar:
1404+
// TODO don't assume signed char
1405+
if (num instanceof Byte)
1406+
return num;
1407+
return Byte.valueOf(num.byteValue());
1408+
case eChar16:
1409+
int intVal = num.intValue();
1410+
int maskedVal = intVal & 0xFFFF;
1411+
if (maskedVal == intVal && num instanceof Integer)
1412+
return num;
1413+
return Integer.valueOf(maskedVal);
1414+
case eChar32:
1415+
long longVal = num.longValue();
1416+
long maskedVal32 = longVal & 0xFFFFFFFFL;
1417+
if (maskedVal32 == longVal && (num instanceof Integer || num instanceof Long))
1418+
return num;
1419+
return Long.valueOf(maskedVal32);
1420+
default:
1421+
return null;
1422+
}
1423+
}
1424+
1425+
return null;
1426+
}
13291427
}

core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecBuiltin.java

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -55,25 +55,35 @@ public ICPPExecution executeForFunctionCall(ActivationRecord record, ConstexprEv
5555

5656
switch (funcId) {
5757
case BUILTIN_FFS:
58+
return executeBuiltinFfs(record, context, intType);
5859
case BUILTIN_FFSL:
60+
return executeBuiltinFfs(record, context, longType);
5961
case BUILTIN_FFSLL:
60-
return executeBuiltinFfs(record, context);
62+
return executeBuiltinFfs(record, context, longlongType);
6163
case BUILTIN_CTZ:
64+
return executeBuiltinCtz(record, context, intType);
6265
case BUILTIN_CTZL:
66+
return executeBuiltinCtz(record, context, longType);
6367
case BUILTIN_CTZLL:
64-
return executeBuiltinCtz(record, context);
68+
return executeBuiltinCtz(record, context, longlongType);
6569
case BUILTIN_POPCOUNT:
70+
return executeBuiltinPopcount(record, context, intType);
6671
case BUILTIN_POPCOUNTL:
72+
return executeBuiltinPopcount(record, context, longType);
6773
case BUILTIN_POPCOUNTLL:
68-
return executeBuiltinPopcount(record, context);
74+
return executeBuiltinPopcount(record, context, longlongType);
6975
case BUILTIN_PARITY:
76+
return executeBuiltinParity(record, context, intType);
7077
case BUILTIN_PARITYL:
78+
return executeBuiltinParity(record, context, longType);
7179
case BUILTIN_PARITYLL:
72-
return executeBuiltinParity(record, context);
80+
return executeBuiltinParity(record, context, longlongType);
7381
case BUILTIN_ABS:
82+
return executeBuiltinAbs(record, context, intType);
7483
case BUILTIN_LABS:
84+
return executeBuiltinAbs(record, context, longType);
7585
case BUILTIN_LLABS:
76-
return executeBuiltinAbs(record, context);
86+
return executeBuiltinAbs(record, context, longlongType);
7787
}
7888
return null;
7989
}
@@ -82,16 +92,18 @@ public ICPPExecution executeForFunctionCall(ActivationRecord record, ConstexprEv
8292
* Return an execution representing __builtin_ffs or __builtin_ctz
8393
*/
8494
private ICPPExecution executeBuiltinFfsCtz(ActivationRecord record, ConstexprEvaluationContext context,
85-
boolean isCtz) {
95+
boolean isCtz, IType argType) {
8696
ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0));
8797

8898
IValue argValue = arg0.getValue();
89-
if (!(argValue instanceof IntegralValue))
99+
Number numberVal = argValue.numberValue();
100+
numberVal = Conversions.narrowNumberValue(numberVal, argType);
101+
if (numberVal == null)
90102
return null;
91103

92104
// __builtin_ffs returns 0 if arg is 0, or 1+count where count is the number of trailing 0 bits
93105
// __builtin_ctz is undefined if arg is 0, or returns count
94-
long arg = argValue.numberValue().longValue();
106+
long arg = numberVal.longValue();
95107
if (arg == 0) {
96108
if (isCtz) {
97109
return null;
@@ -108,26 +120,30 @@ private ICPPExecution executeBuiltinFfsCtz(ActivationRecord record, ConstexprEva
108120
return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(count + increment)));
109121
}
110122

111-
private ICPPExecution executeBuiltinFfs(ActivationRecord record, ConstexprEvaluationContext context) {
112-
return executeBuiltinFfsCtz(record, context, false /* ffs */);
123+
private ICPPExecution executeBuiltinFfs(ActivationRecord record, ConstexprEvaluationContext context,
124+
IType argType) {
125+
return executeBuiltinFfsCtz(record, context, false /* ffs */, argType);
113126
}
114127

115-
private ICPPExecution executeBuiltinCtz(ActivationRecord record, ConstexprEvaluationContext context) {
116-
return executeBuiltinFfsCtz(record, context, true /* ctz */);
128+
private ICPPExecution executeBuiltinCtz(ActivationRecord record, ConstexprEvaluationContext context,
129+
IType argType) {
130+
return executeBuiltinFfsCtz(record, context, true /* ctz */, argType);
117131
}
118132

119133
/*
120134
* Return an execution representing __builtin_popcount
121135
*/
122136
private ICPPExecution executeBuiltinPopcountParity(ActivationRecord record, ConstexprEvaluationContext context,
123-
boolean isParity) {
137+
boolean isParity, IType argType) {
124138
ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0));
125139

126140
IValue argValue = arg0.getValue();
127-
if (!(argValue instanceof IntegralValue))
141+
Number numberVal = argValue.numberValue();
142+
numberVal = Conversions.narrowNumberValue(numberVal, argType);
143+
if (numberVal == null)
128144
return null;
129145

130-
long arg = argValue.numberValue().longValue();
146+
long arg = numberVal.longValue();
131147
int count = 0;
132148
while (arg != 0) {
133149
if ((arg & 1) != 0)
@@ -140,38 +156,30 @@ private ICPPExecution executeBuiltinPopcountParity(ActivationRecord record, Cons
140156
return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(count)));
141157
}
142158

143-
private ICPPExecution executeBuiltinPopcount(ActivationRecord record, ConstexprEvaluationContext context) {
144-
return executeBuiltinPopcountParity(record, context, false);
159+
private ICPPExecution executeBuiltinPopcount(ActivationRecord record, ConstexprEvaluationContext context,
160+
IType argType) {
161+
return executeBuiltinPopcountParity(record, context, false, argType);
145162
}
146163

147-
private ICPPExecution executeBuiltinParity(ActivationRecord record, ConstexprEvaluationContext context) {
148-
return executeBuiltinPopcountParity(record, context, true);
164+
private ICPPExecution executeBuiltinParity(ActivationRecord record, ConstexprEvaluationContext context,
165+
IType argType) {
166+
return executeBuiltinPopcountParity(record, context, true, argType);
149167
}
150168

151-
private ICPPExecution executeBuiltinAbs(ActivationRecord record, ConstexprEvaluationContext context) {
169+
private ICPPExecution executeBuiltinAbs(ActivationRecord record, ConstexprEvaluationContext context,
170+
IType argType) {
152171
ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0));
153172

154173
IValue argValue = arg0.getValue();
155-
if (!(argValue instanceof IntegralValue))
174+
Number argNumber = argValue.numberValue();
175+
argNumber = Conversions.narrowNumberValue(argNumber, argType);
176+
if (argNumber == null)
156177
return null;
157178

158-
long arg = argValue.numberValue().longValue();
179+
long arg = argNumber.longValue();
159180
long result = Math.abs(arg);
160181

161-
IType resultType = null;
162-
switch (funcId) {
163-
case BUILTIN_ABS:
164-
resultType = intType;
165-
break;
166-
case BUILTIN_LABS:
167-
resultType = longType;
168-
break;
169-
case BUILTIN_LLABS:
170-
resultType = longlongType;
171-
break;
172-
}
173-
174-
return new ExecReturn(new EvalFixed(resultType, ValueCategory.PRVALUE, IntegralValue.create(result)));
182+
return new ExecReturn(new EvalFixed(argType, ValueCategory.PRVALUE, IntegralValue.create(result)));
175183
}
176184

177185
@Override

0 commit comments

Comments
 (0)