From 6445ec4890d107e6ad98e8c547d634ee36162425 Mon Sep 17 00:00:00 2001 From: Davin McCall Date: Tue, 23 Aug 2022 02:18:26 +1000 Subject: [PATCH] Bug 580325 - constexpr evaluation of builtins for clrsb, clz (#52) Adds constexpr evaluation for some additional compiler builtins. This is probably the last lot few builtins that can reasonably be constexpr-evaluated in CDT. --- .../cxx14/constexpr/IntegralValueTests.java | 20 +++++ .../dom/parser/GCCBuiltinSymbolProvider.java | 20 +++-- .../dom/parser/cpp/semantics/ExecBuiltin.java | 84 ++++++++++++++++++- 3 files changed, 116 insertions(+), 8 deletions(-) diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/IntegralValueTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/IntegralValueTests.java index 226ac681328..c5848bfeff0 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/IntegralValueTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/IntegralValueTests.java @@ -340,4 +340,24 @@ public abstract class IntegralValueTests extends TestBase { public void testBuiltinAbsNarrowing() throws Exception { assertEvaluationEquals(1); } + + // constexpr int x = __builtin_clrsb(0xFFFFFFFF); + public void testBuiltinClrsb() throws Exception { + assertEvaluationEquals(31); + } + + // constexpr int x = __builtin_clrsb(555); + public void testBuiltinClrsbPositive() throws Exception { + assertEvaluationEquals(21); + } + + // constexpr int x = __builtin_clz(0x8000); + public void testBuiltinClz() throws Exception { + assertEvaluationEquals(16); + } + + // constexpr int x = __builtin_clz(-17); + public void testBuiltinClzNegative() throws Exception { + assertEvaluationEquals(0); + } } \ No newline at end of file diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/GCCBuiltinSymbolProvider.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/GCCBuiltinSymbolProvider.java index 407c02e1d05..70811ca13ac 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/GCCBuiltinSymbolProvider.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/GCCBuiltinSymbolProvider.java @@ -205,6 +205,12 @@ public class GCCBuiltinSymbolProvider implements IBuiltinBindingsProvider { ICPPExecution builtinAbs = new ExecBuiltin(ExecBuiltin.BUILTIN_ABS); ICPPExecution builtinLabs = new ExecBuiltin(ExecBuiltin.BUILTIN_LABS); ICPPExecution builtinLlabs = new ExecBuiltin(ExecBuiltin.BUILTIN_LLABS); + ICPPExecution builtinClrsb = new ExecBuiltin(ExecBuiltin.BUILTIN_CLRSB); + ICPPExecution builtinClrsbl = new ExecBuiltin(ExecBuiltin.BUILTIN_CLRSBL); + ICPPExecution builtinClrsbll = new ExecBuiltin(ExecBuiltin.BUILTIN_CLRSBLL); + ICPPExecution builtinClz = new ExecBuiltin(ExecBuiltin.BUILTIN_CLZ); + ICPPExecution builtinClzl = new ExecBuiltin(ExecBuiltin.BUILTIN_CLZL); + ICPPExecution builtinClzll = new ExecBuiltin(ExecBuiltin.BUILTIN_CLZLL); // Other Builtins (https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html) [incomplete] function("void", "__builtin_abort"); @@ -243,12 +249,12 @@ public class GCCBuiltinSymbolProvider implements IBuiltinBindingsProvider { function("float", "__builtin_cimagf", "complex float"); function("long double", "__builtin_cimagl", "complex long double"); function("void", "__builtin___clear_cache", "void*", "void*"); - function("int", "__builtin_clrsb", "int"); - function("int", "__builtin_clrsbl", "long"); - function("int", "__builtin_clrsbll", "long long"); - function("int", "__builtin_clz", "unsigned int"); - function("int", "__builtin_clzl", "unsigned long"); - function("int", "__builtin_clzll", "unsigned long long"); + function("int", "__builtin_clrsb", builtinClrsb, "int"); + function("int", "__builtin_clrsbl", builtinClrsbl, "long"); + function("int", "__builtin_clrsbll", builtinClrsbll, "long long"); + function("int", "__builtin_clz", builtinClz, "unsigned int"); + function("int", "__builtin_clzl", builtinClzl, "unsigned long"); + function("int", "__builtin_clzll", builtinClzll, "unsigned long long"); function("complex double", "__builtin_conj", "complex double"); function("complex float", "__builtin_conjf", "complex float"); function("complex long double", "__builtin_conjl", "complex long double"); @@ -330,7 +336,7 @@ public class GCCBuiltinSymbolProvider implements IBuiltinBindingsProvider { function("int", "__builtin_ilogb", "double"); function("int", "__builtin_ilogbf", "float"); function("int", "__builtin_ilogbl", "long double"); - function("long long", "__builtin_imaxabs", "long long"); + function("long long", "__builtin_imaxabs", builtinLlabs, "long long"); function("double", "__builtin_inf"); function("_Decimal32", "__builtin_infd32"); function("_Decimal64", "__builtin_infd64"); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecBuiltin.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecBuiltin.java index 86e6aab7ae2..54575701009 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecBuiltin.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecBuiltin.java @@ -19,6 +19,8 @@ import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.IValue; import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer; import org.eclipse.cdt.internal.core.dom.parser.IntegralValue; +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.CPPBuiltinParameter; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation; @@ -33,7 +35,9 @@ import org.eclipse.core.runtime.CoreException; public class ExecBuiltin implements ICPPExecution { public final static short BUILTIN_FFS = 0, BUILTIN_FFSL = 1, BUILTIN_FFSLL = 2, BUILTIN_CTZ = 3, BUILTIN_CTZL = 4, BUILTIN_CTZLL = 5, BUILTIN_POPCOUNT = 6, BUILTIN_POPCOUNTL = 7, BUILTIN_POPCOUNTLL = 8, BUILTIN_PARITY = 9, - BUILTIN_PARITYL = 10, BUILTIN_PARITYLL = 11, BUILTIN_ABS = 12, BUILTIN_LABS = 13, BUILTIN_LLABS = 14; + BUILTIN_PARITYL = 10, BUILTIN_PARITYLL = 11, BUILTIN_ABS = 12, BUILTIN_LABS = 13, BUILTIN_LLABS = 14, + BUILTIN_CLRSB = 15, BUILTIN_CLRSBL = 16, BUILTIN_CLRSBLL = 17, BUILTIN_CLZ = 18, BUILTIN_CLZL = 19, + BUILTIN_CLZLL = 20; private static IType intType = new CPPBasicType(Kind.eInt, 0); private static IType longType = new CPPBasicType(Kind.eInt, CPPBasicType.IS_LONG); @@ -84,6 +88,18 @@ public class ExecBuiltin implements ICPPExecution { return executeBuiltinAbs(record, context, longType); case BUILTIN_LLABS: return executeBuiltinAbs(record, context, longlongType); + case BUILTIN_CLRSB: + return executeBuiltinClrsb(record, context, intType); + case BUILTIN_CLRSBL: + return executeBuiltinClrsb(record, context, longType); + case BUILTIN_CLRSBLL: + return executeBuiltinClrsb(record, context, longlongType); + case BUILTIN_CLZ: + return executeBuiltinClz(record, context, intType); + case BUILTIN_CLZL: + return executeBuiltinClz(record, context, longType); + case BUILTIN_CLZLL: + return executeBuiltinClz(record, context, longlongType); } return null; } @@ -166,6 +182,9 @@ public class ExecBuiltin implements ICPPExecution { return executeBuiltinPopcountParity(record, context, true, argType); } + /* + * Return an execution implementing __builtin_abs + */ private ICPPExecution executeBuiltinAbs(ActivationRecord record, ConstexprEvaluationContext context, IType argType) { ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0)); @@ -182,6 +201,69 @@ public class ExecBuiltin implements ICPPExecution { return new ExecReturn(new EvalFixed(argType, ValueCategory.PRVALUE, IntegralValue.create(result))); } + /* + * Return an execution implementing __builtin_clsrb (count leading "redundant" sign bits) + */ + private ICPPExecution executeBuiltinClrsb(ActivationRecord record, ConstexprEvaluationContext context, + IType argType) { + ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0)); + + SizeAndAlignment sizeToType = SizeofCalculator.getSizeAndAlignment(argType); + if (sizeToType.size > 8) + return null; // can't handle too-large type + + IValue argValue = arg0.getValue(); + Number argNumber = argValue.numberValue(); + if (argNumber == null) + return null; + + long argLong = argNumber.longValue(); + long signBit = 1L << (sizeToType.size * 8 - 1); + int result = 0; + long valueBit = signBit >>> 1; + + if ((argLong & signBit) == 0) { + // if positive, invert all bits so we can unconditionally count 1 bits + argLong = ~argLong; + } + + while ((argLong & valueBit) != 0) { + result++; + valueBit >>>= 1; + } + + return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(result))); + } + + private ICPPExecution executeBuiltinClz(ActivationRecord record, ConstexprEvaluationContext context, + IType argType) { + ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0)); + + SizeAndAlignment sizeToType = SizeofCalculator.getSizeAndAlignment(argType); + if (sizeToType.size > 8) + return null; // can't handle too-large type + + IValue argValue = arg0.getValue(); + Number argNumber = argValue.numberValue(); + if (argNumber == null) + return null; + + long argLong = argNumber.longValue(); + long valueBit = 1L << (sizeToType.size * 8 - 1); + int result = 0; + + // Note that __builtin_clz(0) is supposedly undefined, but GCC (12.1) apparently returns + // the bit size of int, so we'll make sure to do the same. + while ((argLong & valueBit) == 0) { + result++; + valueBit >>>= 1; + if (valueBit == 0) + break; + } + + return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(result))); + } + @Override public void marshal(ITypeMarshalBuffer buffer, boolean includeValue) throws CoreException { buffer.putShort(ITypeMarshalBuffer.EXEC_BUILTIN);