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 43bf85a6144..48160677923 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 @@ -269,4 +269,9 @@ public abstract class IntegralValueTests extends TestBase { public void testHexLiteral() throws Exception { assertEvaluationEquals(42); } + + // constexpr int x = __builtin_ffs(0) + __builtin_ffs(16); + public void testBuiltinFfs() throws Exception { + assertEvaluationEquals(5); + } } \ No newline at end of file diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java index 1070ee2f554..79badd6a337 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/cxx14/constexpr/TestBase.java @@ -21,7 +21,7 @@ import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IValue; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause; import org.eclipse.cdt.core.dom.parser.IScannerExtensionConfiguration; -import org.eclipse.cdt.core.dom.parser.cpp.ANSICPPParserExtensionConfiguration; +import org.eclipse.cdt.core.dom.parser.cpp.GPPParserExtensionConfiguration; import org.eclipse.cdt.core.dom.parser.cpp.GPPScannerExtensionConfiguration; import org.eclipse.cdt.core.dom.parser.cpp.ICPPParserExtensionConfiguration; import org.eclipse.cdt.core.index.IIndex; @@ -194,7 +194,7 @@ public abstract class TestBase extends IndexBindingResolutionTestBase { IScanner scanner = createScanner(FileContent.create(TEST_CODE, code.toCharArray()), ParserLanguage.CPP, ParserMode.COMPLETE_PARSE, SCANNER_INFO); AbstractGNUSourceCodeParser parser = null; - ICPPParserExtensionConfiguration config = new ANSICPPParserExtensionConfiguration(); + ICPPParserExtensionConfiguration config = new GPPParserExtensionConfiguration(); parser = new GNUCPPSourceParser(scanner, ParserMode.COMPLETE_PARSE, NULL_LOG, config, null); parser.setMaximumTrivialExpressionsInAggregateInitializers(Integer.MAX_VALUE); 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 b453dfe5e10..b5ab4d3b5df 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 @@ -53,6 +53,8 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitTypedef; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPQualifierType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPReferenceType; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPExecution; +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecBuiltin; /** * This is the IBuiltinBindingsProvider used to implement the "Other" built-in GCC symbols defined: @@ -188,6 +190,8 @@ public class GCCBuiltinSymbolProvider implements IBuiltinBindingsProvider { function("bool", "__atomic_always_lock_free", "size_t", "void*"); function("bool", "__atomic_is_lock_free", "size_t", "void*"); + ICPPExecution builtinFfs = new ExecBuiltin(ExecBuiltin.BUILTIN_FFS); + // Other Builtins (https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html) [incomplete] function("void", "__builtin_abort"); function("int", "__builtin_abs", "int"); @@ -277,9 +281,9 @@ public class GCCBuiltinSymbolProvider implements IBuiltinBindingsProvider { function("double", "__builtin_fdim", "double", "double"); function("float", "__builtin_fdimf", "float", "float"); function("long double", "__builtin_fdiml", "long double", "long double"); - function("int", "__builtin_ffs", "unsigned int"); - function("int", "__builtin_ffsl", "unsigned long"); - function("int", "__builtin_ffsll", "unsigned long long"); + function("int", "__builtin_ffs", builtinFfs, "unsigned int"); + function("int", "__builtin_ffsl", builtinFfs, "unsigned long"); + function("int", "__builtin_ffsll", builtinFfs, "unsigned long long"); function("double", "__builtin_floor", "double"); function("float", "__builtin_floorf", "float"); function("long double", "__builtin_floorl", "long double"); @@ -501,6 +505,13 @@ public class GCCBuiltinSymbolProvider implements IBuiltinBindingsProvider { } private void function(String returnType, String name, String... parameterTypes) { + function(returnType, name, null, parameterTypes); + } + + /* + * Create a function which can possibly be constexpr-evaluated + */ + private void function(String returnType, String name, ICPPExecution exec, String... parameterTypes) { int len = parameterTypes.length; boolean varargs = len > 0 && parameterTypes[len - 1].equals("..."); if (varargs) @@ -511,14 +522,14 @@ public class GCCBuiltinSymbolProvider implements IBuiltinBindingsProvider { for (int i = 0; i < len; i++) { IType pType = toType(parameterTypes[i]); pTypes[i] = pType; - theParms[i] = fCpp ? new CPPBuiltinParameter(pType) : new CBuiltinParameter(pType); + theParms[i] = fCpp ? new CPPBuiltinParameter(pType, i) : new CBuiltinParameter(pType); } IType rt = toType(returnType); IFunctionType ft = fCpp ? new CPPFunctionType(rt, pTypes, null) : new CFunctionType(rt, pTypes); IBinding b = fCpp - ? new CPPImplicitFunction(toCharArray(name), fScope, (ICPPFunctionType) ft, (ICPPParameter[]) theParms, - false, varargs) + ? new CPPBuiltinImplicitFunction(toCharArray(name), fScope, (ICPPFunctionType) ft, + (ICPPParameter[]) theParms, varargs, exec) : new CImplicitFunction(toCharArray(name), fScope, ft, theParms, varargs); fBindingList.add(b); } @@ -661,4 +672,22 @@ public class GCCBuiltinSymbolProvider implements IBuiltinBindingsProvider { public boolean isKnownBuiltin(char[] builtinName) { return fKnownBuiltins.containsKey(builtinName); } + + /* + * A builtin function which can be evaluated in a constexpr context + */ + private static class CPPBuiltinImplicitFunction extends CPPImplicitFunction { + private ICPPExecution execution; + + public CPPBuiltinImplicitFunction(char[] name, IScope scope, ICPPFunctionType type, ICPPParameter[] params, + boolean takesVarArgs, ICPPExecution execution) { + super(name, scope, type, params, true, takesVarArgs); + this.execution = execution; + } + + @Override + public ICPPExecution getFunctionBodyExecution() { + return execution; + } + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ITypeMarshalBuffer.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ITypeMarshalBuffer.java index cc215747ed2..b7e352d7bf1 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ITypeMarshalBuffer.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ITypeMarshalBuffer.java @@ -52,7 +52,7 @@ public interface ITypeMarshalBuffer { EXEC_DECLARATION_STATEMENT = 0x05, EXEC_DECLARATOR = 0x06, EXEC_DEFAULT = 0x07, EXEC_SIMPLE_DECLARATION = 0x08, EXEC_RETURN = 0x09, EXEC_EXPRESSION_STATEMENT = 0x0A, EXEC_IF = 0x0B, EXEC_WHILE = 0x0C, EXEC_DO = 0x0D, EXEC_FOR = 0x0E, EXEC_RANGE_BASED_FOR = 0x0F, EXEC_SWITCH = 0x10, - EXEC_CONSTRUCTOR_CHAIN = 0x11, EXEC_INCOMPLETE = 0x12; + EXEC_CONSTRUCTOR_CHAIN = 0x11, EXEC_INCOMPLETE = 0x12, EXEC_BUILTIN = 0x13; // Can add more executions up to 0x1C, after that it will collide with TypeMarshalBuffer.UNSTORABLE_TYPE. static final short KIND_MASK = 0x001F; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPBuiltinParameter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPBuiltinParameter.java index 3dd21dc3de9..754da65a108 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPBuiltinParameter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPBuiltinParameter.java @@ -38,9 +38,16 @@ public class CPPBuiltinParameter extends PlatformObject implements ICPPParameter } private IType type; + private int position; public CPPBuiltinParameter(IType type) { this.type = type; + this.position = -1; + } + + public CPPBuiltinParameter(IType type, int position) { + this.type = type; + this.position = position; } @Override @@ -75,6 +82,9 @@ public class CPPBuiltinParameter extends PlatformObject implements ICPPParameter @Override public String getName() { + if (position != -1) { + return "arg" + position; //$NON-NLS-1$ + } return ""; //$NON-NLS-1$ } 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 new file mode 100644 index 00000000000..338ef00240f --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/ExecBuiltin.java @@ -0,0 +1,89 @@ +/******************************************************************************* +* Copyright (c) 2022 Davin McCall and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Davin McCall - initial API and implementation +*******************************************************************************/ +package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics; + +import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory; +import org.eclipse.cdt.core.dom.ast.IBasicType.Kind; +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.cpp.CPPBasicType; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBuiltinParameter; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation.ConstexprEvaluationContext; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPExecution; +import org.eclipse.cdt.internal.core.dom.parser.cpp.InstantiationContext; +import org.eclipse.core.runtime.CoreException; + +/** + * Constexpr-evaluation for compiler builtin functions. + */ +public class ExecBuiltin implements ICPPExecution { + public final static short BUILTIN_FFS = 0; + + private static IType intType = new CPPBasicType(Kind.eInt, 0); + + private short funcId; + + public ExecBuiltin(short funcId) { + this.funcId = funcId; + } + + @Override + public ICPPExecution instantiate(InstantiationContext context, int maxDepth) { + return this; + } + + @Override + public ICPPExecution executeForFunctionCall(ActivationRecord record, ConstexprEvaluationContext context) { + + switch (funcId) { + case BUILTIN_FFS: + return executeBuiltinFfs(record, context); + } + return null; + } + + private ICPPExecution executeBuiltinFfs(ActivationRecord record, ConstexprEvaluationContext context) { + ICPPEvaluation arg0 = record.getVariable(new CPPBuiltinParameter(null, 0)); + + IValue argValue = arg0.getValue(); + if (!(argValue instanceof IntegralValue)) + return null; + + // __builtin_ffs returns 0 if arg is 0, or 1+count where count is the number of trailing 0 bits + long arg = argValue.numberValue().longValue(); + if (arg == 0) { + return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(0))); + } + int count = 0; + while ((arg & 1) == 0) { + arg >>= 1; + count++; + } + return new ExecReturn(new EvalFixed(intType, ValueCategory.PRVALUE, IntegralValue.create(count + 1))); + } + + @Override + public void marshal(ITypeMarshalBuffer buffer, boolean includeValue) throws CoreException { + buffer.putShort(ITypeMarshalBuffer.EXEC_BUILTIN); + buffer.putShort(funcId); + } + + public static ICPPExecution unmarshal(short firstBytes, ITypeMarshalBuffer buffer) throws CoreException { + short funcId = buffer.getShort(); + return new ExecBuiltin(funcId); + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java index 9b683b08a2e..991f3ef73eb 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java @@ -143,6 +143,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalTypeId; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalUnary; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalUnaryTypeID; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecBreak; +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecBuiltin; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecCase; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecCompoundStatement; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExecConstructorChain; @@ -1771,6 +1772,8 @@ class PDOMCPPLinkage extends PDOMLinkage implements IIndexCPPBindingConstants { return ExecConstructorChain.unmarshal(firstBytes, buffer); case ITypeMarshalBuffer.EXEC_INCOMPLETE: return ExecIncomplete.unmarshal(firstBytes, buffer); + case ITypeMarshalBuffer.EXEC_BUILTIN: + return ExecBuiltin.unmarshal(firstBytes, buffer); } throw new CoreException(CCorePlugin.createStatus("Cannot unmarshal an execution, first bytes=" + firstBytes)); //$NON-NLS-1$ }