1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-21 21:52:10 +02:00

Bug 580201 - narrowing for constexpr evaluation of builtins

Implement narrowing of argument value (in constexpr context) for various
builtins.

Change-Id: I428cc789358638bf3796ea706f459032c8be10a1
This commit is contained in:
Davin McCall 2022-06-18 17:27:59 +10:00
parent b1011b0a82
commit 4a956060cd
5 changed files with 171 additions and 36 deletions

View file

@ -113,6 +113,7 @@ public class AST2TestBase extends SemanticTestBase {
map.put("__SIZEOF_SHORT__", "2"); map.put("__SIZEOF_SHORT__", "2");
map.put("__SIZEOF_INT__", "4"); map.put("__SIZEOF_INT__", "4");
map.put("__SIZEOF_LONG__", "8"); map.put("__SIZEOF_LONG__", "8");
map.put("__SIZEOF_LONG_LONG__", "8");
map.put("__SIZEOF_DOUBLE__", "8"); map.put("__SIZEOF_DOUBLE__", "8");
map.put("__SIZEOF_POINTER__", "8"); map.put("__SIZEOF_POINTER__", "8");
return map; return map;
@ -123,6 +124,7 @@ public class AST2TestBase extends SemanticTestBase {
map.put("__SIZEOF_SHORT__", "2"); map.put("__SIZEOF_SHORT__", "2");
map.put("__SIZEOF_INT__", "4"); map.put("__SIZEOF_INT__", "4");
map.put("__SIZEOF_LONG__", "8"); map.put("__SIZEOF_LONG__", "8");
map.put("__SIZEOF_LONG_LONG__", "8");
map.put("__SIZEOF_DOUBLE__", "8"); map.put("__SIZEOF_DOUBLE__", "8");
map.put("__SIZEOF_POINTER__", "8"); map.put("__SIZEOF_POINTER__", "8");
return map; return map;

View file

@ -11,6 +11,7 @@
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2.cxx14.constexpr; package org.eclipse.cdt.core.parser.tests.ast2.cxx14.constexpr;
import org.eclipse.cdt.core.testplugin.TestScannerProvider;
import org.eclipse.cdt.internal.core.dom.parser.IntegralValue; import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
import junit.framework.TestSuite; import junit.framework.TestSuite;
@ -36,6 +37,16 @@ public abstract class IntegralValueTests extends TestBase {
} }
} }
@Override
protected void setUp() throws Exception {
TestScannerProvider.sDefinedSymbols.put("__SIZEOF_SHORT__", "2");
TestScannerProvider.sDefinedSymbols.put("__SIZEOF_INT__", "4");
TestScannerProvider.sDefinedSymbols.put("__SIZEOF_LONG__", "8");
TestScannerProvider.sDefinedSymbols.put("__SIZEOF_LONG_LONG__", "8");
TestScannerProvider.sDefinedSymbols.put("__SIZEOF_POINTER__", "8");
super.setUp();
}
// constexpr auto x = int{} + int(); // constexpr auto x = int{} + int();
public void testIntDefaultValue() throws Exception { public void testIntDefaultValue() throws Exception {
assertEvaluationEquals(0); assertEvaluationEquals(0);
@ -275,6 +286,16 @@ public abstract class IntegralValueTests extends TestBase {
assertEvaluationEquals(5); assertEvaluationEquals(5);
} }
// constexpr int x = __builtin_ffs(0x100000000);
public void testBuiltinFfsNarrowing() throws Exception {
assertEvaluationEquals(0);
}
// constexpr int x = __builtin_ffsl(0x100000000);
public void testBuiltinFfsl() throws Exception {
assertEvaluationEquals(33);
}
// constexpr int x = __builtin_ctz(16); // constexpr int x = __builtin_ctz(16);
public void testBuiltinCtz() throws Exception { public void testBuiltinCtz() throws Exception {
assertEvaluationEquals(4); assertEvaluationEquals(4);
@ -314,4 +335,9 @@ public abstract class IntegralValueTests extends TestBase {
public void testBuiltinAbsNegativeInput() throws Exception { public void testBuiltinAbsNegativeInput() throws Exception {
assertEvaluationEquals(1); assertEvaluationEquals(1);
} }
// constexpr int x = __builtin_abs(0xFFFFFFFF);
public void testBuiltinAbsNarrowing() throws Exception {
assertEvaluationEquals(1);
}
} }

View file

@ -66,6 +66,7 @@ public abstract class TestBase extends IndexBindingResolutionTestBase {
map.put("__SIZEOF_SHORT__", "2"); map.put("__SIZEOF_SHORT__", "2");
map.put("__SIZEOF_INT__", "4"); map.put("__SIZEOF_INT__", "4");
map.put("__SIZEOF_LONG__", "8"); map.put("__SIZEOF_LONG__", "8");
map.put("__SIZEOF_LONG_LONG__", "8");
map.put("__SIZEOF_POINTER__", "8"); map.put("__SIZEOF_POINTER__", "8");
return map; return map;
} }

View file

@ -30,6 +30,7 @@ import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUti
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getNestedType; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getNestedType;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.isVoidType; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.isVoidType;
import java.math.BigInteger;
import java.util.Collections; import java.util.Collections;
import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.DOMException;
@ -60,6 +61,8 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.dom.parser.ArithmeticConversion; import org.eclipse.cdt.internal.core.dom.parser.ArithmeticConversion;
import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer; import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer;
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator;
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator.SizeAndAlignment;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerToMemberType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerToMemberType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType;
@ -1326,4 +1329,99 @@ public class Conversions {
private static boolean isNullPtr(IType t1) { private static boolean isNullPtr(IType t1) {
return t1 instanceof IBasicType && ((IBasicType) t1).getKind() == Kind.eNullPtr; return t1 instanceof IBasicType && ((IBasicType) t1).getKind() == Kind.eNullPtr;
} }
/**
* Narrow a numeric value to the range of a specified type.
* @param num the value to narrow (may be null)
* @param toType the type to narrow to
* @return a number representing the narrowed value, or null
*/
public static Number narrowNumberValue(Number num, IType toType) {
if (num == null)
return null;
if (toType instanceof IBasicType) {
IBasicType basicType = (IBasicType) toType;
IBasicType.Kind basicTypeKind = basicType.getKind();
switch (basicTypeKind) {
case eFloat:
if (num instanceof Float)
return num;
return Float.valueOf(num.floatValue());
case eDouble:
if (num instanceof Double)
return num;
return Double.valueOf(num.doubleValue());
case eInt:
SizeAndAlignment sizeToType = SizeofCalculator.getSizeAndAlignment(toType);
if (sizeToType == null)
return null;
// Note in the following we don't check type.isSigned() since that checks for the
// explicit presence of the "signed" modifier. So instead check !type.isUnsigned().
if (sizeToType.size <= 8) {
// First, mask the value to the correct size
// Note that we take the longValue here which may be negative even though the
// original value is positive; the masking here should still be correct and we
// should ultimately end up with the correct narrowed value, regardless.
long longVal = num.longValue();
long maskVal = 0xFFFFFFFFFFFFFFFFL;
long signBit = 0x8000000000000000L;
// Calculate a mask to reduce the size of the value to the target width:
maskVal >>>= (8 - sizeToType.size) * 8;
signBit >>>= (8 - sizeToType.size) * 8;
if (!basicType.isUnsigned() && (longVal & signBit) != 0) {
// We need to extend the sign bit.
long signBits = ~maskVal;
longVal |= signBits;
} else {
longVal &= maskVal;
}
// The Java type used to store the numerical value is independent of the associated
// C type, but we go with a smaller type (Integer) where possible. For 4 bytes
// (signed) or less than 4 bytes (signed or not) we can use Integer. For 8 bytes
// (signed) or less than 8 bytes (signed or not) we can use Long. Any larger and we
// resort to BigInteger.
if (longVal >= 0 && longVal <= Integer.MAX_VALUE) {
return Integer.valueOf((int) longVal);
}
if (!basicType.isUnsigned() && longVal >= Integer.MIN_VALUE && longVal <= Integer.MAX_VALUE) {
return Integer.valueOf((int) longVal);
}
if (!basicType.isUnsigned() || longVal > 0) {
return Long.valueOf(longVal);
}
BigInteger biVal = BigInteger.valueOf(longVal);
// 2**64 = 18446744073709551616
biVal = biVal.add(new BigInteger("18446744073709551616")); //$NON-NLS-1$
return biVal;
}
// TODO handle larger int sizes?
return null;
case eChar:
// TODO don't assume signed char
if (num instanceof Byte)
return num;
return Byte.valueOf(num.byteValue());
case eChar16:
int intVal = num.intValue();
int maskedVal = intVal & 0xFFFF;
if (maskedVal == intVal && num instanceof Integer)
return num;
return Integer.valueOf(maskedVal);
case eChar32:
long longVal = num.longValue();
long maskedVal32 = longVal & 0xFFFFFFFFL;
if (maskedVal32 == longVal && (num instanceof Integer || num instanceof Long))
return num;
return Long.valueOf(maskedVal32);
default:
return null;
}
}
return null;
}
} }

View file

@ -55,25 +55,35 @@ public class ExecBuiltin implements ICPPExecution {
switch (funcId) { switch (funcId) {
case BUILTIN_FFS: case BUILTIN_FFS:
return executeBuiltinFfs(record, context, intType);
case BUILTIN_FFSL: case BUILTIN_FFSL:
return executeBuiltinFfs(record, context, longType);
case BUILTIN_FFSLL: case BUILTIN_FFSLL:
return executeBuiltinFfs(record, context); return executeBuiltinFfs(record, context, longlongType);
case BUILTIN_CTZ: case BUILTIN_CTZ:
return executeBuiltinCtz(record, context, intType);
case BUILTIN_CTZL: case BUILTIN_CTZL:
return executeBuiltinCtz(record, context, longType);
case BUILTIN_CTZLL: case BUILTIN_CTZLL:
return executeBuiltinCtz(record, context); return executeBuiltinCtz(record, context, longlongType);
case BUILTIN_POPCOUNT: case BUILTIN_POPCOUNT:
return executeBuiltinPopcount(record, context, intType);
case BUILTIN_POPCOUNTL: case BUILTIN_POPCOUNTL:
return executeBuiltinPopcount(record, context, longType);
case BUILTIN_POPCOUNTLL: case BUILTIN_POPCOUNTLL:
return executeBuiltinPopcount(record, context); return executeBuiltinPopcount(record, context, longlongType);
case BUILTIN_PARITY: case BUILTIN_PARITY:
return executeBuiltinParity(record, context, intType);
case BUILTIN_PARITYL: case BUILTIN_PARITYL:
return executeBuiltinParity(record, context, longType);
case BUILTIN_PARITYLL: case BUILTIN_PARITYLL:
return executeBuiltinParity(record, context); return executeBuiltinParity(record, context, longlongType);
case BUILTIN_ABS: case BUILTIN_ABS:
return executeBuiltinAbs(record, context, intType);
case BUILTIN_LABS: case BUILTIN_LABS:
return executeBuiltinAbs(record, context, longType);
case BUILTIN_LLABS: case BUILTIN_LLABS:
return executeBuiltinAbs(record, context); return executeBuiltinAbs(record, context, longlongType);
} }
return null; return null;
} }
@ -82,16 +92,18 @@ public class ExecBuiltin implements ICPPExecution {
* Return an execution representing __builtin_ffs or __builtin_ctz * Return an execution representing __builtin_ffs or __builtin_ctz
*/ */
private ICPPExecution executeBuiltinFfsCtz(ActivationRecord record, ConstexprEvaluationContext context, private ICPPExecution executeBuiltinFfsCtz(ActivationRecord record, ConstexprEvaluationContext context,
boolean isCtz) { boolean isCtz, IType argType) {
ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0)); ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0));
IValue argValue = arg0.getValue(); IValue argValue = arg0.getValue();
if (!(argValue instanceof IntegralValue)) Number numberVal = argValue.numberValue();
numberVal = Conversions.narrowNumberValue(numberVal, argType);
if (numberVal == null)
return null; return null;
// __builtin_ffs returns 0 if arg is 0, or 1+count where count is the number of trailing 0 bits // __builtin_ffs returns 0 if arg is 0, or 1+count where count is the number of trailing 0 bits
// __builtin_ctz is undefined if arg is 0, or returns count // __builtin_ctz is undefined if arg is 0, or returns count
long arg = argValue.numberValue().longValue(); long arg = numberVal.longValue();
if (arg == 0) { if (arg == 0) {
if (isCtz) { if (isCtz) {
return null; return null;
@ -108,26 +120,30 @@ public class ExecBuiltin implements ICPPExecution {
return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(count + increment))); return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(count + increment)));
} }
private ICPPExecution executeBuiltinFfs(ActivationRecord record, ConstexprEvaluationContext context) { private ICPPExecution executeBuiltinFfs(ActivationRecord record, ConstexprEvaluationContext context,
return executeBuiltinFfsCtz(record, context, false /* ffs */); IType argType) {
return executeBuiltinFfsCtz(record, context, false /* ffs */, argType);
} }
private ICPPExecution executeBuiltinCtz(ActivationRecord record, ConstexprEvaluationContext context) { private ICPPExecution executeBuiltinCtz(ActivationRecord record, ConstexprEvaluationContext context,
return executeBuiltinFfsCtz(record, context, true /* ctz */); IType argType) {
return executeBuiltinFfsCtz(record, context, true /* ctz */, argType);
} }
/* /*
* Return an execution representing __builtin_popcount * Return an execution representing __builtin_popcount
*/ */
private ICPPExecution executeBuiltinPopcountParity(ActivationRecord record, ConstexprEvaluationContext context, private ICPPExecution executeBuiltinPopcountParity(ActivationRecord record, ConstexprEvaluationContext context,
boolean isParity) { boolean isParity, IType argType) {
ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0)); ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0));
IValue argValue = arg0.getValue(); IValue argValue = arg0.getValue();
if (!(argValue instanceof IntegralValue)) Number numberVal = argValue.numberValue();
numberVal = Conversions.narrowNumberValue(numberVal, argType);
if (numberVal == null)
return null; return null;
long arg = argValue.numberValue().longValue(); long arg = numberVal.longValue();
int count = 0; int count = 0;
while (arg != 0) { while (arg != 0) {
if ((arg & 1) != 0) if ((arg & 1) != 0)
@ -140,38 +156,30 @@ public class ExecBuiltin implements ICPPExecution {
return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(count))); return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(count)));
} }
private ICPPExecution executeBuiltinPopcount(ActivationRecord record, ConstexprEvaluationContext context) { private ICPPExecution executeBuiltinPopcount(ActivationRecord record, ConstexprEvaluationContext context,
return executeBuiltinPopcountParity(record, context, false); IType argType) {
return executeBuiltinPopcountParity(record, context, false, argType);
} }
private ICPPExecution executeBuiltinParity(ActivationRecord record, ConstexprEvaluationContext context) { private ICPPExecution executeBuiltinParity(ActivationRecord record, ConstexprEvaluationContext context,
return executeBuiltinPopcountParity(record, context, true); IType argType) {
return executeBuiltinPopcountParity(record, context, true, argType);
} }
private ICPPExecution executeBuiltinAbs(ActivationRecord record, ConstexprEvaluationContext context) { private ICPPExecution executeBuiltinAbs(ActivationRecord record, ConstexprEvaluationContext context,
IType argType) {
ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0)); ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0));
IValue argValue = arg0.getValue(); IValue argValue = arg0.getValue();
if (!(argValue instanceof IntegralValue)) Number argNumber = argValue.numberValue();
argNumber = Conversions.narrowNumberValue(argNumber, argType);
if (argNumber == null)
return null; return null;
long arg = argValue.numberValue().longValue(); long arg = argNumber.longValue();
long result = Math.abs(arg); long result = Math.abs(arg);
IType resultType = null; return new ExecReturn(new EvalFixed(argType, ValueCategory.PRVALUE, IntegralValue.create(result)));
switch (funcId) {
case BUILTIN_ABS:
resultType = intType;
break;
case BUILTIN_LABS:
resultType = longType;
break;
case BUILTIN_LLABS:
resultType = longlongType;
break;
}
return new ExecReturn(new EvalFixed(resultType, ValueCategory.PRVALUE, IntegralValue.create(result)));
} }
@Override @Override