diff options
author | David Olsen <dolsen@nvidia.com> | 2025-02-19 19:58:12 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-02-19 19:58:12 -0800 |
commit | f8bdbed5b3f12c5e1c38a29cd71df286a9e2725c (patch) | |
tree | 41d8287ec05c55a2dc344123a43c43253e0d7f30 /clang/lib/CIR/CodeGen | |
parent | 1fd280d0f2a31a41e74374c7757a8a4d01e116b5 (diff) | |
download | llvm-f8bdbed5b3f12c5e1c38a29cd71df286a9e2725c.zip llvm-f8bdbed5b3f12c5e1c38a29cd71df286a9e2725c.tar.gz llvm-f8bdbed5b3f12c5e1c38a29cd71df286a9e2725c.tar.bz2 |
[CIR] Upstream simple function bodies (#127674)
Enable ClangIR generation for very simple functions. The functions have
to return `void` or an integral type, contain only compound statements
or `return` statements, and `return` statement expressions can only be
integral literals of the correct type. The functions can have
parameters, but those are currently ignored because there is no way to
access them.
This change intentionally focuses on breadth (introducing scopes,
statements, and expressions) rather than depth, because it enables
people to work on upstreaming in parallel without interference.
The new ClangIR ops in this change are `ReturnOp`, `YieldOp`, `ScopeOp`,
and `TrapOp`. These operations are complete (except for the
`ParentOneOf` property) and shouldn't require further upstreaming
changes. Significant additions were made to `FuncOp`, adding a type and
a region, but that operation is still far from complete.
The classes `ScalarExprEmitter` and `CIRGenFunction`, along with the
`emit*` functions in `CIRGenFunction` that generate ClangIR for
statements, are new in this change. All of these are very incomplete and
will be filled out in later upstreaming patches.
Existing test `hello.c` is removed and replaced by the new test
`func-simple.cpp`. This tests all forms of functions that are currently
supported.
Diffstat (limited to 'clang/lib/CIR/CodeGen')
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 70 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 220 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.h | 141 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 74 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.h | 26 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 128 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 21 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenTypes.h | 14 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CMakeLists.txt | 3 |
9 files changed, 687 insertions, 10 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp new file mode 100644 index 0000000..b802705 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Emit Expr nodes with scalar CIR types as CIR code. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenFunction.h" + +#include "clang/AST/Expr.h" +#include "clang/AST/StmtVisitor.h" + +#include "mlir/IR/Value.h" + +#include <cassert> + +using namespace clang; +using namespace clang::CIRGen; + +namespace { + +class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { + CIRGenFunction &cgf; + CIRGenBuilderTy &builder; + bool ignoreResultAssign; + +public: + ScalarExprEmitter(CIRGenFunction &cgf, CIRGenBuilderTy &builder, + bool ira = false) + : cgf(cgf), builder(builder), ignoreResultAssign(ira) {} + + //===--------------------------------------------------------------------===// + // Visitor Methods + //===--------------------------------------------------------------------===// + + mlir::Value Visit(Expr *e) { + return StmtVisitor<ScalarExprEmitter, mlir::Value>::Visit(e); + } + + mlir::Value VisitStmt(Stmt *s) { + llvm_unreachable("Statement passed to ScalarExprEmitter"); + } + + mlir::Value VisitExpr(Expr *e) { + cgf.getCIRGenModule().errorNYI( + e->getSourceRange(), "scalar expression kind: ", e->getStmtClassName()); + return {}; + } + + mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) { + mlir::Type type = cgf.convertType(e->getType()); + return builder.create<cir::ConstantOp>( + cgf.getLoc(e->getExprLoc()), type, + builder.getAttr<cir::IntAttr>(type, e->getValue())); + } +}; +} // namespace + +/// Emit the computation of the specified expression of scalar type. +mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { + assert(e && hasScalarEvaluationKind(e->getType()) && + "Invalid scalar expression to emit"); + + return ScalarExprEmitter(*this, builder).Visit(const_cast<Expr *>(e)); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp new file mode 100644 index 0000000..bba2f71 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -0,0 +1,220 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Internal per-function state used for AST-to-ClangIR code gen +// +//===----------------------------------------------------------------------===// + +#include "CIRGenFunction.h" + +#include "clang/AST/GlobalDecl.h" + +#include <cassert> + +namespace clang::CIRGen { + +CIRGenFunction::CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder, + bool suppressNewContext) + : CIRGenTypeCache(cgm), cgm{cgm}, builder(builder) {} + +CIRGenFunction::~CIRGenFunction() {} + +// This is copied from clang/lib/CodeGen/CodeGenFunction.cpp +cir::TypeEvaluationKind CIRGenFunction::getEvaluationKind(QualType type) { + type = type.getCanonicalType(); + while (true) { + switch (type->getTypeClass()) { +#define TYPE(name, parent) +#define ABSTRACT_TYPE(name, parent) +#define NON_CANONICAL_TYPE(name, parent) case Type::name: +#define DEPENDENT_TYPE(name, parent) case Type::name: +#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(name, parent) case Type::name: +#include "clang/AST/TypeNodes.inc" + llvm_unreachable("non-canonical or dependent type in IR-generation"); + + case Type::ArrayParameter: + case Type::HLSLAttributedResource: + llvm_unreachable("NYI"); + + case Type::Auto: + case Type::DeducedTemplateSpecialization: + llvm_unreachable("undeduced type in IR-generation"); + + // Various scalar types. + case Type::Builtin: + case Type::Pointer: + case Type::BlockPointer: + case Type::LValueReference: + case Type::RValueReference: + case Type::MemberPointer: + case Type::Vector: + case Type::ExtVector: + case Type::ConstantMatrix: + case Type::FunctionProto: + case Type::FunctionNoProto: + case Type::Enum: + case Type::ObjCObjectPointer: + case Type::Pipe: + case Type::BitInt: + return cir::TEK_Scalar; + + // Complexes. + case Type::Complex: + return cir::TEK_Complex; + + // Arrays, records, and Objective-C objects. + case Type::ConstantArray: + case Type::IncompleteArray: + case Type::VariableArray: + case Type::Record: + case Type::ObjCObject: + case Type::ObjCInterface: + return cir::TEK_Aggregate; + + // We operate on atomic values according to their underlying type. + case Type::Atomic: + type = cast<AtomicType>(type)->getValueType(); + continue; + } + llvm_unreachable("unknown type kind!"); + } +} + +mlir::Type CIRGenFunction::convertTypeForMem(QualType t) { + return cgm.getTypes().convertTypeForMem(t); +} + +mlir::Type CIRGenFunction::convertType(QualType t) { + return cgm.getTypes().convertType(t); +} + +mlir::Location CIRGenFunction::getLoc(SourceLocation srcLoc) { + // Some AST nodes might contain invalid source locations (e.g. + // CXXDefaultArgExpr), workaround that to still get something out. + if (srcLoc.isValid()) { + const SourceManager &sm = getContext().getSourceManager(); + PresumedLoc pLoc = sm.getPresumedLoc(srcLoc); + StringRef filename = pLoc.getFilename(); + return mlir::FileLineColLoc::get(builder.getStringAttr(filename), + pLoc.getLine(), pLoc.getColumn()); + } + // Do our best... + assert(currSrcLoc && "expected to inherit some source location"); + return *currSrcLoc; +} + +mlir::Location CIRGenFunction::getLoc(SourceRange srcLoc) { + // Some AST nodes might contain invalid source locations (e.g. + // CXXDefaultArgExpr), workaround that to still get something out. + if (srcLoc.isValid()) { + mlir::Location beg = getLoc(srcLoc.getBegin()); + mlir::Location end = getLoc(srcLoc.getEnd()); + SmallVector<mlir::Location, 2> locs = {beg, end}; + mlir::Attribute metadata; + return mlir::FusedLoc::get(locs, metadata, &getMLIRContext()); + } + if (currSrcLoc) { + return *currSrcLoc; + } + // We're brave, but time to give up. + return builder.getUnknownLoc(); +} + +mlir::Location CIRGenFunction::getLoc(mlir::Location lhs, mlir::Location rhs) { + SmallVector<mlir::Location, 2> locs = {lhs, rhs}; + mlir::Attribute metadata; + return mlir::FusedLoc::get(locs, metadata, &getMLIRContext()); +} + +void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, + cir::FuncOp fn, cir::FuncType funcType, + SourceLocation loc, + SourceLocation startLoc) { + assert(!curFn && + "CIRGenFunction can only be used for one function at a time"); + + fnRetTy = returnType; + curFn = fn; + + mlir::Block *entryBB = &fn.getBlocks().front(); + builder.setInsertionPointToStart(entryBB); +} + +void CIRGenFunction::finishFunction(SourceLocation endLoc) {} + +mlir::LogicalResult CIRGenFunction::emitFunctionBody(const clang::Stmt *body) { + auto result = mlir::LogicalResult::success(); + if (const CompoundStmt *block = dyn_cast<CompoundStmt>(body)) + emitCompoundStmtWithoutScope(*block); + else + result = emitStmt(body, /*useCurrentScope=*/true); + return result; +} + +cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, + cir::FuncType funcType) { + const auto funcDecl = cast<FunctionDecl>(gd.getDecl()); + SourceLocation loc = funcDecl->getLocation(); + Stmt *body = funcDecl->getBody(); + SourceRange bodyRange = + body ? body->getSourceRange() : funcDecl->getLocation(); + + SourceLocRAIIObject fnLoc{*this, loc.isValid() ? getLoc(loc) + : builder.getUnknownLoc()}; + + // This will be used once more code is upstreamed. + [[maybe_unused]] mlir::Block *entryBB = fn.addEntryBlock(); + + startFunction(gd, funcDecl->getReturnType(), fn, funcType, loc, + bodyRange.getBegin()); + + if (isa<CXXDestructorDecl>(funcDecl)) + getCIRGenModule().errorNYI(bodyRange, "C++ destructor definition"); + else if (isa<CXXConstructorDecl>(funcDecl)) + getCIRGenModule().errorNYI(bodyRange, "C++ constructor definition"); + else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice && + funcDecl->hasAttr<CUDAGlobalAttr>()) + getCIRGenModule().errorNYI(bodyRange, "CUDA kernel"); + else if (isa<CXXMethodDecl>(funcDecl) && + cast<CXXMethodDecl>(funcDecl)->isLambdaStaticInvoker()) + getCIRGenModule().errorNYI(bodyRange, "Lambda static invoker"); + else if (funcDecl->isDefaulted() && isa<CXXMethodDecl>(funcDecl) && + (cast<CXXMethodDecl>(funcDecl)->isCopyAssignmentOperator() || + cast<CXXMethodDecl>(funcDecl)->isMoveAssignmentOperator())) + getCIRGenModule().errorNYI(bodyRange, "Default assignment operator"); + else if (body) { + if (mlir::failed(emitFunctionBody(body))) { + fn.erase(); + return nullptr; + } + } else + llvm_unreachable("no definition for normal function"); + + // This code to insert a cir.return or cir.trap at the end of the function is + // temporary until the function return code, including + // CIRGenFunction::LexicalScope::emitImplicitReturn(), is upstreamed. + mlir::Block &lastBlock = fn.getRegion().back(); + if (lastBlock.empty() || !lastBlock.mightHaveTerminator() || + !lastBlock.getTerminator()->hasTrait<mlir::OpTrait::IsTerminator>()) { + builder.setInsertionPointToEnd(&lastBlock); + if (mlir::isa<cir::VoidType>(funcType.getReturnType())) { + builder.create<cir::ReturnOp>(getLoc(bodyRange.getEnd())); + } else { + builder.create<cir::TrapOp>(getLoc(bodyRange.getEnd())); + } + } + + if (mlir::failed(fn.verifyBody())) + return nullptr; + + finishFunction(bodyRange.getEnd()); + + return fn; +} + +} // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h new file mode 100644 index 0000000..92fbea1 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -0,0 +1,141 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Internal per-function state used for AST-to-ClangIR code gen +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_LIB_CIR_CODEGEN_CIRGENFUNCTION_H +#define CLANG_LIB_CIR_CODEGEN_CIRGENFUNCTION_H + +#include "CIRGenBuilder.h" +#include "CIRGenModule.h" +#include "CIRGenTypeCache.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Type.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/TypeEvaluationKind.h" + +#include "llvm/ADT/ScopedHashTable.h" + +namespace { +class ScalarExprEmitter; +} // namespace + +namespace clang::CIRGen { + +class CIRGenFunction : public CIRGenTypeCache { +public: + CIRGenModule &cgm; + +private: + friend class ::ScalarExprEmitter; + /// The builder is a helper class to create IR inside a function. The + /// builder is stateful, in particular it keeps an "insertion point": this + /// is where the next operations will be introduced. + CIRGenBuilderTy &builder; + +public: + clang::QualType fnRetTy; + + /// This is the current function or global initializer that is generated code + /// for. + mlir::Operation *curFn = nullptr; + + clang::ASTContext &getContext() const { return cgm.getASTContext(); } + + CIRGenBuilderTy &getBuilder() { return builder; } + + CIRGenModule &getCIRGenModule() { return cgm; } + const CIRGenModule &getCIRGenModule() const { return cgm; } + + mlir::Type convertTypeForMem(QualType T); + + mlir::Type convertType(clang::QualType T); + mlir::Type convertType(const TypeDecl *T) { + return convertType(getContext().getTypeDeclType(T)); + } + + /// Return the cir::TypeEvaluationKind of QualType \c type. + static cir::TypeEvaluationKind getEvaluationKind(clang::QualType type); + + static bool hasScalarEvaluationKind(clang::QualType type) { + return getEvaluationKind(type) == cir::TEK_Scalar; + } + + CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder, + bool suppressNewContext = false); + ~CIRGenFunction(); + + CIRGenTypes &getTypes() const { return cgm.getTypes(); } + + mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); } + + /// Use to track source locations across nested visitor traversals. + /// Always use a `SourceLocRAIIObject` to change currSrcLoc. + std::optional<mlir::Location> currSrcLoc; + class SourceLocRAIIObject { + CIRGenFunction &cgf; + std::optional<mlir::Location> oldLoc; + + public: + SourceLocRAIIObject(CIRGenFunction &cgf, mlir::Location value) : cgf(cgf) { + if (cgf.currSrcLoc) + oldLoc = cgf.currSrcLoc; + cgf.currSrcLoc = value; + } + + /// Can be used to restore the state early, before the dtor + /// is run. + void restore() { cgf.currSrcLoc = oldLoc; } + ~SourceLocRAIIObject() { restore(); } + }; + + /// Helpers to convert Clang's SourceLocation to a MLIR Location. + mlir::Location getLoc(clang::SourceLocation srcLoc); + mlir::Location getLoc(clang::SourceRange srcLoc); + mlir::Location getLoc(mlir::Location lhs, mlir::Location rhs); + + const clang::LangOptions &getLangOpts() const { return cgm.getLangOpts(); } + + void finishFunction(SourceLocation endLoc); + mlir::LogicalResult emitFunctionBody(const clang::Stmt *body); + + // Build CIR for a statement. useCurrentScope should be true if no + // new scopes need be created when finding a compound statement. + mlir::LogicalResult + emitStmt(const clang::Stmt *s, bool useCurrentScope, + llvm::ArrayRef<const Attr *> attrs = std::nullopt); + + mlir::LogicalResult emitSimpleStmt(const clang::Stmt *s, + bool useCurrentScope); + + void emitCompoundStmt(const clang::CompoundStmt &s); + + void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s); + + mlir::LogicalResult emitReturnStmt(const clang::ReturnStmt &s); + + /// Emit the computation of the specified expression of scalar type. + mlir::Value emitScalarExpr(const clang::Expr *e); + cir::FuncOp generateCode(clang::GlobalDecl gd, cir::FuncOp fn, + cir::FuncType funcType); + + /// Emit code for the start of a function. + /// \param loc The location to be associated with the function. + /// \param startLoc The location of the function body. + void startFunction(clang::GlobalDecl gd, clang::QualType retTy, + cir::FuncOp fn, cir::FuncType funcType, + clang::SourceLocation loc, clang::SourceLocation startLoc); +}; + +} // namespace clang::CIRGen + +#endif diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index cbecdf9..c1d3265 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "CIRGenModule.h" +#include "CIRGenFunction.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclBase.h" @@ -102,19 +103,30 @@ void CIRGenModule::emitGlobal(clang::GlobalDecl gd) { void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd, mlir::Operation *op) { auto const *funcDecl = cast<FunctionDecl>(gd.getDecl()); - if (clang::IdentifierInfo *identifier = funcDecl->getIdentifier()) { - auto funcOp = builder.create<cir::FuncOp>( - getLoc(funcDecl->getSourceRange()), identifier->getName()); - theModule.push_back(funcOp); - } else { + if (funcDecl->getIdentifier() == nullptr) { errorNYI(funcDecl->getSourceRange().getBegin(), "function definition with a non-identifier for a name"); + return; + } + cir::FuncType funcType = + cast<cir::FuncType>(convertType(funcDecl->getType())); + + cir::FuncOp funcOp = dyn_cast_if_present<cir::FuncOp>(op); + if (!funcOp || funcOp.getFunctionType() != funcType) { + funcOp = getAddrOfFunction(gd, funcType, /*ForVTable=*/false, + /*DontDefer=*/true, ForDefinition); + } + + CIRGenFunction cgf(*this, builder); + { + mlir::OpBuilder::InsertionGuard guard(builder); + cgf.generateCode(gd, funcOp, funcType); } } void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd, bool isTentative) { - mlir::Type type = getTypes().convertType(vd->getType()); + mlir::Type type = convertType(vd->getType()); if (clang::IdentifierInfo *identifier = vd->getIdentifier()) { auto varOp = builder.create<cir::GlobalOp>(getLoc(vd->getSourceRange()), identifier->getName(), type); @@ -223,6 +235,56 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { } } +cir::FuncOp CIRGenModule::getAddrOfFunction(clang::GlobalDecl gd, + mlir::Type funcType, bool forVTable, + bool dontDefer, + ForDefinition_t isForDefinition) { + assert(!cast<FunctionDecl>(gd.getDecl())->isConsteval() && + "consteval function should never be emitted"); + + if (!funcType) { + const auto *fd = cast<FunctionDecl>(gd.getDecl()); + funcType = convertType(fd->getType()); + } + + cir::FuncOp func = getOrCreateCIRFunction( + cast<NamedDecl>(gd.getDecl())->getIdentifier()->getName(), funcType, gd, + forVTable, dontDefer, /*isThunk=*/false, isForDefinition); + return func; +} + +cir::FuncOp CIRGenModule::getOrCreateCIRFunction( + StringRef mangledName, mlir::Type funcType, GlobalDecl gd, bool forVTable, + bool dontDefer, bool isThunk, ForDefinition_t isForDefinition, + mlir::ArrayAttr extraAttrs) { + auto *funcDecl = llvm::cast_or_null<FunctionDecl>(gd.getDecl()); + bool invalidLoc = !funcDecl || + funcDecl->getSourceRange().getBegin().isInvalid() || + funcDecl->getSourceRange().getEnd().isInvalid(); + cir::FuncOp funcOp = createCIRFunction( + invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()), + mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl); + return funcOp; +} + +cir::FuncOp +CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, + cir::FuncType funcType, + const clang::FunctionDecl *funcDecl) { + cir::FuncOp func; + { + mlir::OpBuilder::InsertionGuard guard(builder); + + func = builder.create<cir::FuncOp>(loc, name, funcType); + theModule.push_back(func); + } + return func; +} + +mlir::Type CIRGenModule::convertType(QualType type) { + return genTypes.convertType(type); +} + DiagnosticBuilder CIRGenModule::errorNYI(SourceLocation loc, llvm::StringRef feature) { unsigned diagID = diags.getCustomDiagID( diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 29bb403..bf3a4d1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -17,6 +17,8 @@ #include "CIRGenTypeCache.h" #include "CIRGenTypes.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" + #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/MLIRContext.h" @@ -36,6 +38,8 @@ class VarDecl; namespace CIRGen { +enum ForDefinition_t : bool { NotForDefinition = false, ForDefinition = true }; + /// This class organizes the cross-function state that is used while generating /// CIR code. class CIRGenModule : public CIRGenTypeCache { @@ -71,6 +75,7 @@ public: CIRGenBuilderTy &getBuilder() { return builder; } clang::ASTContext &getASTContext() const { return astContext; } CIRGenTypes &getTypes() { return genTypes; } + const clang::LangOptions &getLangOpts() const { return langOpts; } mlir::MLIRContext &getMLIRContext() { return *builder.getContext(); } /// Helpers to convert the presumed location of Clang's SourceLocation to an @@ -80,16 +85,37 @@ public: void emitTopLevelDecl(clang::Decl *decl); + /// Return the address of the given function. If funcType is non-null, then + /// this function will use the specified type if it has to create it. + // TODO: this is a bit weird as `GetAddr` given we give back a FuncOp? + cir::FuncOp + getAddrOfFunction(clang::GlobalDecl gd, mlir::Type funcType = nullptr, + bool forVTable = false, bool dontDefer = false, + ForDefinition_t isForDefinition = NotForDefinition); + /// Emit code for a single global function or variable declaration. Forward /// declarations are emitted lazily. void emitGlobal(clang::GlobalDecl gd); + mlir::Type convertType(clang::QualType type); + void emitGlobalDefinition(clang::GlobalDecl gd, mlir::Operation *op = nullptr); void emitGlobalFunctionDefinition(clang::GlobalDecl gd, mlir::Operation *op); void emitGlobalVarDefinition(const clang::VarDecl *vd, bool isTentative = false); + cir::FuncOp + getOrCreateCIRFunction(llvm::StringRef mangledName, mlir::Type funcType, + clang::GlobalDecl gd, bool forVTable, + bool dontDefer = false, bool isThunk = false, + ForDefinition_t isForDefinition = NotForDefinition, + mlir::ArrayAttr extraAttrs = {}); + + cir::FuncOp createCIRFunction(mlir::Location loc, llvm::StringRef name, + cir::FuncType funcType, + const clang::FunctionDecl *funcDecl); + const llvm::Triple &getTriple() const { return target.getTriple(); } /// Helpers to emit "not yet implemented" error diagnostics diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp new file mode 100644 index 0000000..f42f30c --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -0,0 +1,128 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Emit Stmt nodes as CIR code. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenBuilder.h" +#include "CIRGenFunction.h" + +#include "mlir/IR/Builders.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Stmt.h" + +using namespace clang; +using namespace clang::CIRGen; +using namespace cir; + +void CIRGenFunction::emitCompoundStmtWithoutScope(const CompoundStmt &s) { + for (auto *curStmt : s.body()) { + if (emitStmt(curStmt, /*useCurrentScope=*/false).failed()) + getCIRGenModule().errorNYI(curStmt->getSourceRange(), "statement"); + } +} + +void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) { + mlir::Location scopeLoc = getLoc(s.getSourceRange()); + auto scope = builder.create<cir::ScopeOp>( + scopeLoc, [&](mlir::OpBuilder &b, mlir::Type &type, mlir::Location loc) { + emitCompoundStmtWithoutScope(s); + }); + + // This code to insert a cir.yield at the end of the scope is temporary until + // CIRGenFunction::LexicalScope::cleanup() is upstreamed. + if (!scope.getRegion().empty()) { + mlir::Block &lastBlock = scope.getRegion().back(); + if (lastBlock.empty() || !lastBlock.mightHaveTerminator() || + !lastBlock.getTerminator()->hasTrait<mlir::OpTrait::IsTerminator>()) { + builder.setInsertionPointToEnd(&lastBlock); + builder.create<cir::YieldOp>(getLoc(s.getEndLoc())); + } + } +} + +// Build CIR for a statement. useCurrentScope should be true if no new scopes +// need to be created when finding a compound statement. +mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s, + bool useCurrentScope, + ArrayRef<const Attr *> attr) { + if (mlir::succeeded(emitSimpleStmt(s, useCurrentScope))) + return mlir::success(); + + // Only a subset of simple statements are supported at the moment. When more + // kinds of statements are supported, a + // switch (s->getStmtClass()) { + // will be added here. + return mlir::failure(); +} + +mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s, + bool useCurrentScope) { + switch (s->getStmtClass()) { + default: + // Only compound and return statements are supported right now. + return mlir::failure(); + case Stmt::CompoundStmtClass: + if (useCurrentScope) + emitCompoundStmtWithoutScope(cast<CompoundStmt>(*s)); + else + emitCompoundStmt(cast<CompoundStmt>(*s)); + break; + case Stmt::ReturnStmtClass: + return emitReturnStmt(cast<ReturnStmt>(*s)); + } + + return mlir::success(); +} + +mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) { + mlir::Location loc = getLoc(s.getSourceRange()); + const Expr *rv = s.getRetValue(); + + if (getContext().getLangOpts().ElideConstructors && s.getNRVOCandidate() && + s.getNRVOCandidate()->isNRVOVariable()) { + getCIRGenModule().errorNYI(s.getSourceRange(), + "named return value optimization"); + } else if (!rv) { + // No return expression. Do nothing. + // TODO(CIR): In the future when function returns are fully implemented, + // this section will do nothing. But for now a ReturnOp is necessary. + builder.create<ReturnOp>(loc); + } else if (rv->getType()->isVoidType()) { + // No return value. Emit the return expression for its side effects. + // TODO(CIR): Once emitAnyExpr(e) has been upstreamed, get rid of the check + // and just call emitAnyExpr(rv) here. + if (CIRGenFunction::hasScalarEvaluationKind(rv->getType())) { + emitScalarExpr(rv); + } else { + getCIRGenModule().errorNYI(s.getSourceRange(), + "non-scalar function return type"); + } + builder.create<ReturnOp>(loc); + } else if (fnRetTy->isReferenceType()) { + getCIRGenModule().errorNYI(s.getSourceRange(), + "function return type that is a reference"); + } else { + mlir::Value value = nullptr; + switch (CIRGenFunction::getEvaluationKind(rv->getType())) { + case cir::TEK_Scalar: + value = emitScalarExpr(rv); + if (value) { // Change this to an assert once emitScalarExpr is complete + builder.create<ReturnOp>(loc, llvm::ArrayRef(value)); + } + break; + default: + getCIRGenModule().errorNYI(s.getSourceRange(), + "non-scalar function return type"); + break; + } + } + + return mlir::success(); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 8519854..551b43e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -3,6 +3,7 @@ #include "CIRGenModule.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/GlobalDecl.h" #include "clang/AST/Type.h" #include "clang/Basic/TargetInfo.h" @@ -56,7 +57,7 @@ bool CIRGenTypes::isFuncTypeConvertible(const FunctionType *ft) { return true; } -mlir::Type CIRGenTypes::ConvertFunctionTypeInternal(QualType qft) { +mlir::Type CIRGenTypes::convertFunctionTypeInternal(QualType qft) { assert(qft.isCanonical()); const FunctionType *ft = cast<FunctionType>(qft.getTypePtr()); // First, check whether we can build the full fucntion type. If the function @@ -198,7 +199,7 @@ mlir::Type CIRGenTypes::convertType(QualType type) { case Type::FunctionNoProto: case Type::FunctionProto: - resultType = ConvertFunctionTypeInternal(type); + resultType = convertFunctionTypeInternal(type); break; case Type::BitInt: { @@ -224,3 +225,19 @@ mlir::Type CIRGenTypes::convertType(QualType type) { typeCache[ty] = resultType; return resultType; } + +mlir::Type CIRGenTypes::convertTypeForMem(clang::QualType qualType, + bool forBitField) { + assert(!qualType->isConstantMatrixType() && "Matrix types NYI"); + + mlir::Type convertedType = convertType(qualType); + + assert(!forBitField && "Bit fields NYI"); + + // If this is a bit-precise integer type in a bitfield representation, map + // this integer to the target-specified size. + if (forBitField && qualType->isBitIntType()) + assert(!qualType->isBitIntType() && "Bit field with type _BitInt NYI"); + + return convertedType; +} diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h index 71427e1..f280e17e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h @@ -15,11 +15,14 @@ #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/AST/Type.h" + #include "llvm/ADT/SmallPtrSet.h" namespace clang { class ASTContext; class FunctionType; +class GlobalDecl; class QualType; class Type; } // namespace clang @@ -40,8 +43,8 @@ class CIRGenTypes { clang::ASTContext &astContext; CIRGenBuilderTy &builder; - /// Heper for ConvertType. - mlir::Type ConvertFunctionTypeInternal(clang::QualType ft); + /// Heper for convertType. + mlir::Type convertFunctionTypeInternal(clang::QualType ft); public: CIRGenTypes(CIRGenModule &cgm); @@ -61,6 +64,13 @@ public: /// Convert a Clang type into a mlir::Type. mlir::Type convertType(clang::QualType type); + + /// Convert type T into an mlir::Type. This differs from convertType in that + /// it is used to convert to the memory representation for a type. For + /// example, the scalar representation for bool is i1, but the memory + /// representation is usually i8 or i32, depending on the target. + // TODO: convert this comment to account for MLIR's equivalence + mlir::Type convertTypeForMem(clang::QualType, bool forBitField = false); }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 782b814..5602efa 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -8,7 +8,10 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) add_clang_library(clangCIR CIRGenerator.cpp + CIRGenExprScalar.cpp + CIRGenFunction.cpp CIRGenModule.cpp + CIRGenStmt.cpp CIRGenTypes.cpp DEPENDS |