diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -73,6 +73,7 @@ Interp/EvalEmitter.cpp Interp/Frame.cpp Interp/Function.cpp + Interp/InterpBuiltin.cpp Interp/Interp.cpp Interp/InterpBlock.cpp Interp/InterpFrame.cpp diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -63,6 +63,7 @@ bool VisitPointerArithBinOp(const BinaryOperator *E); bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E); bool VisitCallExpr(const CallExpr *E); + bool VisitBuiltinCallExpr(const CallExpr *E); bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *E); bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E); bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E); diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1240,9 +1240,35 @@ return false; } +template +bool ByteCodeExprGen::VisitBuiltinCallExpr(const CallExpr *E) { + const Function *Func = getFunction(E->getDirectCallee()); + if (!Func) + return false; + + // Put arguments on the stack. + for (const auto *Arg : E->arguments()) { + if (!this->visit(Arg)) + return false; + } + + if (!this->emitCallBI(Func, E)) + return false; + + if (DiscardResult) { + QualType ReturnType = E->getCallReturnType(Ctx.getASTContext()); + PrimType T = classifyPrim(ReturnType); + + return this->emitPop(T, E); + } + + return true; +} + template bool ByteCodeExprGen::VisitCallExpr(const CallExpr *E) { - assert(!E->getBuiltinCallee() && "Builtin functions aren't supported yet"); + if (E->getBuiltinCallee()) + return VisitBuiltinCallExpr(E); const Decl *Callee = E->getCalleeDecl(); if (const auto *FuncDecl = dyn_cast_or_null(Callee)) { @@ -1276,9 +1302,9 @@ return false; } - // In any case call the function. The return value will end up on the stack and - // if the function has RVO, we already have the pointer on the stack to write - // the result into. + // In any case call the function. The return value will end up on the stack + // and if the function has RVO, we already have the pointer on the stack to + // write the result into. if (!this->emitCall(Func, E)) return false; diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -138,6 +138,8 @@ // Checks if the funtion already has a body attached. bool hasBody() const { return HasBody; } + unsigned getBuiltinID() const { return F->getBuiltinID(); } + unsigned getNumParams() const { return ParamTypes.size(); } private: diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -140,6 +140,8 @@ /// Interpreter entry point. bool Interpret(InterpState &S, APValue &Result); +bool InterpretBuiltin(InterpState &S, CodePtr PC, unsigned BuiltinID); + enum class ArithOp { Add, Sub }; //===----------------------------------------------------------------------===// @@ -1320,6 +1322,20 @@ return false; } +inline bool CallBI(InterpState &S, CodePtr &PC, const Function *Func) { + auto NewFrame = std::make_unique(S, Func, PC); + + InterpFrame *FrameBefore = S.Current; + S.Current = NewFrame.get(); + + if (InterpretBuiltin(S, PC, Func->getBuiltinID())) { + NewFrame.release(); + return true; + } + S.Current = FrameBefore; + return false; +} + //===----------------------------------------------------------------------===// // Read opcode arguments //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -0,0 +1,51 @@ +//===--- InterpBuiltin.cpp - Interpreter for the constexpr VM ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "Boolean.h" +#include "Interp.h" +#include "PrimType.h" +#include "clang/Basic/Builtins.h" + +namespace clang { +namespace interp { + +/// This is a slightly simplified version of the Ret() we have in Interp.cpp +/// If they end up diverging in the future, we should get rid of the code +/// duplication. +template ::T> +static bool Ret(InterpState &S, CodePtr &PC) { + S.CallStackDepth--; + const T &Ret = S.Stk.pop(); + + assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame"); + if (!S.checkingPotentialConstantExpression()) + S.Current->popArgs(); + + InterpFrame *Caller = S.Current->Caller; + assert(Caller); + + PC = S.Current->getRetPC(); + delete S.Current; + S.Current = Caller; + S.Stk.push(Ret); + + return true; +} + +bool InterpretBuiltin(InterpState &S, CodePtr PC, unsigned BuiltinID) { + switch (BuiltinID) { + case Builtin::BI__builtin_is_constant_evaluated: + S.Stk.push(Boolean::from(S.inConstantContext())); + Ret(S, PC); + return true; + } + + return false; +} + +} // namespace interp +} // namespace clang diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -164,6 +164,12 @@ let ChangesPC = 1; } +def CallBI : Opcode { + let Args = [ArgFunction]; + let Types = []; + let ChangesPC = 1; +} + //===----------------------------------------------------------------------===// // Frame management //===----------------------------------------------------------------------===// diff --git a/clang/test/AST/Interp/builtins.cpp b/clang/test/AST/Interp/builtins.cpp new file mode 100644 --- /dev/null +++ b/clang/test/AST/Interp/builtins.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -verify +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -S -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated +// RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated %s -S -emit-llvm -o - | FileCheck %s + +// expected-no-diagnostics +// ref-no-diagnostics + +using size_t = decltype(sizeof(int)); + +namespace std { +inline constexpr bool is_constant_evaluated() noexcept { + return __builtin_is_constant_evaluated(); +} +} // namespace std + +constexpr bool b = std::is_constant_evaluated(); +static_assert(b, ""); +static_assert(std::is_constant_evaluated() , ""); + + +bool is_this_constant() { + return __builtin_is_constant_evaluated(); // CHECK: ret i1 false +}