mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-19 00:56:41 +00:00
[CIR] Emit init of local variables (#130164)
Local variable initialization was previously being ignored. This change adds support for initialization of scalar variables with constant values and introduces the constant emitter framework.
This commit is contained in:
parent
5668c7bb90
commit
8eb9b947af
@ -26,6 +26,10 @@ public:
|
||||
CIRBaseBuilderTy(mlir::MLIRContext &mlirContext)
|
||||
: mlir::OpBuilder(&mlirContext) {}
|
||||
|
||||
cir::ConstantOp getConstant(mlir::Location loc, mlir::TypedAttr attr) {
|
||||
return create<cir::ConstantOp>(loc, attr.getType(), attr);
|
||||
}
|
||||
|
||||
cir::ConstantOp getBool(bool state, mlir::Location loc) {
|
||||
return create<cir::ConstantOp>(loc, getBoolTy(), getCIRBoolAttr(state));
|
||||
}
|
||||
|
@ -54,6 +54,20 @@ def CIR_BoolAttr : CIR_Attr<"Bool", "bool", [TypedAttrInterface]> {
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ZeroAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def ZeroAttr : CIR_Attr<"Zero", "zero", [TypedAttrInterface]> {
|
||||
let summary = "Attribute to represent zero initialization";
|
||||
let description = [{
|
||||
The ZeroAttr is used to indicate zero initialization on structs.
|
||||
}];
|
||||
|
||||
let parameters = (ins AttributeSelfTypeParameter<"">:$type);
|
||||
let assemblyFormat = [{}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// UndefAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -41,16 +41,17 @@ struct MissingFeatures {
|
||||
static bool supportComdat() { return false; }
|
||||
|
||||
// Load/store attributes
|
||||
static bool opLoadThreadLocal() { return false; }
|
||||
static bool opLoadStoreThreadLocal() { return false; }
|
||||
static bool opLoadEmitScalarRangeCheck() { return false; }
|
||||
static bool opLoadBooleanRepresentation() { return false; }
|
||||
static bool opLoadStoreTbaa() { return false; }
|
||||
static bool opLoadStoreMemOrder() { return false; }
|
||||
static bool opLoadStoreVolatile() { return false; }
|
||||
static bool opLoadStoreAlignment() { return false; }
|
||||
static bool opLoadStoreAtomic() { return false; }
|
||||
static bool opLoadStoreObjC() { return false; }
|
||||
|
||||
// AllocaOp handling
|
||||
static bool opAllocaVarDeclContext() { return false; }
|
||||
static bool opAllocaStaticLocal() { return false; }
|
||||
static bool opAllocaNonGC() { return false; }
|
||||
static bool opAllocaImpreciseLifetime() { return false; }
|
||||
@ -61,6 +62,7 @@ struct MissingFeatures {
|
||||
static bool opAllocaReference() { return false; }
|
||||
static bool opAllocaAnnotations() { return false; }
|
||||
static bool opAllocaDynAllocSize() { return false; }
|
||||
static bool opAllocaCaptureByInit() { return false; }
|
||||
|
||||
// FuncOp handling
|
||||
static bool opFuncOpenCLKernelMetadata() { return false; }
|
||||
@ -76,6 +78,10 @@ struct MissingFeatures {
|
||||
static bool constructABIArgDirectExtend() { return false; }
|
||||
static bool opGlobalViewAttr() { return false; }
|
||||
static bool lowerModeOptLevel() { return false; }
|
||||
static bool opTBAA() { return false; }
|
||||
static bool objCLifetime() { return false; }
|
||||
static bool emitNullabilityCheck() { return false; }
|
||||
static bool astVarDeclInterface() { return false; }
|
||||
};
|
||||
|
||||
} // namespace cir
|
||||
|
100
clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
Normal file
100
clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
Normal file
@ -0,0 +1,100 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// A helper class for emitting expressions and values as cir::ConstantOp
|
||||
// and as initializers for global variables.
|
||||
//
|
||||
// Note: this is based on clang's LLVM IR codegen in ConstantEmitter.h, reusing
|
||||
// this class interface makes it easier move forward with bringing CIR codegen
|
||||
// to completion.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef CLANG_LIB_CIR_CODEGEN_CIRGENCONSTANTEMITTER_H
|
||||
#define CLANG_LIB_CIR_CODEGEN_CIRGENCONSTANTEMITTER_H
|
||||
|
||||
#include "CIRGenFunction.h"
|
||||
#include "CIRGenModule.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
namespace clang::CIRGen {
|
||||
|
||||
class ConstantEmitter {
|
||||
public:
|
||||
CIRGenModule &cgm;
|
||||
const CIRGenFunction *cgf;
|
||||
|
||||
private:
|
||||
bool abstract = false;
|
||||
|
||||
/// Whether we're in a constant context.
|
||||
bool inConstantContext = false;
|
||||
|
||||
public:
|
||||
/// Initialize this emission in the context of the given function.
|
||||
/// Use this if the expression might contain contextual references like
|
||||
/// block addresses or PredefinedExprs.
|
||||
ConstantEmitter(CIRGenFunction &cgf) : cgm(cgf.cgm), cgf(&cgf) {}
|
||||
|
||||
ConstantEmitter(const ConstantEmitter &other) = delete;
|
||||
ConstantEmitter &operator=(const ConstantEmitter &other) = delete;
|
||||
|
||||
// All of the "abstract" emission methods below permit the emission to
|
||||
// be immediately discarded without finalizing anything. Therefore, they
|
||||
// must also promise not to do anything that will, in the future, require
|
||||
// finalization:
|
||||
//
|
||||
// - using the CGF (if present) for anything other than establishing
|
||||
// semantic context; for example, an expression with ignored
|
||||
// side-effects must not be emitted as an abstract expression
|
||||
//
|
||||
// - doing anything that would not be safe to duplicate within an
|
||||
// initializer or to propagate to another context; for example,
|
||||
// side effects, or emitting an initialization that requires a
|
||||
// reference to its current location.
|
||||
mlir::Attribute emitForMemory(mlir::Attribute c, QualType t);
|
||||
|
||||
/// Emit the result of the given expression as an abstract constant,
|
||||
/// asserting that it succeeded. This is only safe to do when the
|
||||
/// expression is known to be a constant expression with either a fairly
|
||||
/// simple type or a known simple form.
|
||||
mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value,
|
||||
QualType t);
|
||||
|
||||
mlir::Attribute tryEmitConstantExpr(const ConstantExpr *CE);
|
||||
|
||||
// These are private helper routines of the constant emitter that
|
||||
// can't actually be private because things are split out into helper
|
||||
// functions and classes.
|
||||
|
||||
mlir::Attribute tryEmitPrivateForVarInit(const VarDecl &d);
|
||||
|
||||
mlir::Attribute tryEmitPrivate(const APValue &value, QualType destType);
|
||||
mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType t);
|
||||
|
||||
/// Try to emit the initializer of the given declaration as an abstract
|
||||
/// constant.
|
||||
mlir::Attribute tryEmitAbstractForInitializer(const VarDecl &d);
|
||||
|
||||
private:
|
||||
class AbstractStateRAII {
|
||||
ConstantEmitter &emitter;
|
||||
bool oldValue;
|
||||
|
||||
public:
|
||||
AbstractStateRAII(ConstantEmitter &emitter, bool value)
|
||||
: emitter(emitter), oldValue(emitter.abstract) {
|
||||
emitter.abstract = value;
|
||||
}
|
||||
~AbstractStateRAII() { emitter.abstract = oldValue; }
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace clang::CIRGen
|
||||
|
||||
#endif // CLANG_LIB_CIR_CODEGEN_CIRGENCONSTANTEMITTER_H
|
@ -10,23 +10,29 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CIRGenConstantEmitter.h"
|
||||
#include "CIRGenFunction.h"
|
||||
#include "mlir/IR/Location.h"
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/CIR/MissingFeatures.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::CIRGen;
|
||||
|
||||
void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
|
||||
CIRGenFunction::AutoVarEmission
|
||||
CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
|
||||
QualType ty = d.getType();
|
||||
if (ty.getAddressSpace() != LangAS::Default)
|
||||
cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: address space");
|
||||
|
||||
auto loc = getLoc(d.getSourceRange());
|
||||
mlir::Location loc = getLoc(d.getSourceRange());
|
||||
|
||||
if (d.isEscapingByref())
|
||||
CIRGenFunction::AutoVarEmission emission(d);
|
||||
emission.IsEscapingByRef = d.isEscapingByref();
|
||||
if (emission.IsEscapingByRef)
|
||||
cgm.errorNYI(d.getSourceRange(),
|
||||
"emitAutoVarDecl: decl escaping by reference");
|
||||
|
||||
@ -46,21 +52,130 @@ void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
|
||||
address = createTempAlloca(allocaTy, alignment, loc, d.getName());
|
||||
declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment);
|
||||
|
||||
emission.Addr = address;
|
||||
setAddrOfLocalVar(&d, address);
|
||||
|
||||
return emission;
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitAutoVarInit(const clang::VarDecl &d) {
|
||||
/// Determine whether the given initializer is trivial in the sense
|
||||
/// that it requires no code to be generated.
|
||||
bool CIRGenFunction::isTrivialInitializer(const Expr *init) {
|
||||
if (!init)
|
||||
return true;
|
||||
|
||||
if (const CXXConstructExpr *construct = dyn_cast<CXXConstructExpr>(init))
|
||||
if (CXXConstructorDecl *constructor = construct->getConstructor())
|
||||
if (constructor->isTrivial() && constructor->isDefaultConstructor() &&
|
||||
!construct->requiresZeroInitialization())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitAutoVarInit(
|
||||
const CIRGenFunction::AutoVarEmission &emission) {
|
||||
assert(emission.Variable && "emission was not valid!");
|
||||
|
||||
// If this was emitted as a global constant, we're done.
|
||||
if (emission.wasEmittedAsGlobal())
|
||||
return;
|
||||
|
||||
const VarDecl &d = *emission.Variable;
|
||||
|
||||
QualType type = d.getType();
|
||||
|
||||
// If this local has an initializer, emit it now.
|
||||
const Expr *init = d.getInit();
|
||||
|
||||
if (init || !type.isPODType(getContext())) {
|
||||
cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit");
|
||||
if (!type.isPODType(getContext())) {
|
||||
cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: non-POD type");
|
||||
return;
|
||||
}
|
||||
|
||||
const Address addr = emission.Addr;
|
||||
|
||||
// Check whether this is a byref variable that's potentially
|
||||
// captured and moved by its own initializer. If so, we'll need to
|
||||
// emit the initializer first, then copy into the variable.
|
||||
assert(!cir::MissingFeatures::opAllocaCaptureByInit());
|
||||
|
||||
// Note: constexpr already initializes everything correctly.
|
||||
LangOptions::TrivialAutoVarInitKind trivialAutoVarInit =
|
||||
(d.isConstexpr()
|
||||
? LangOptions::TrivialAutoVarInitKind::Uninitialized
|
||||
: (d.getAttr<UninitializedAttr>()
|
||||
? LangOptions::TrivialAutoVarInitKind::Uninitialized
|
||||
: getContext().getLangOpts().getTrivialAutoVarInit()));
|
||||
|
||||
auto initializeWhatIsTechnicallyUninitialized = [&](Address addr) {
|
||||
if (trivialAutoVarInit ==
|
||||
LangOptions::TrivialAutoVarInitKind::Uninitialized)
|
||||
return;
|
||||
|
||||
cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: trivial initialization");
|
||||
};
|
||||
|
||||
if (isTrivialInitializer(init)) {
|
||||
initializeWhatIsTechnicallyUninitialized(addr);
|
||||
return;
|
||||
}
|
||||
|
||||
mlir::Attribute constant;
|
||||
if (emission.IsConstantAggregate ||
|
||||
d.mightBeUsableInConstantExpressions(getContext())) {
|
||||
// FIXME: Differently from LLVM we try not to emit / lower too much
|
||||
// here for CIR since we are interested in seeing the ctor in some
|
||||
// analysis later on. So CIR's implementation of ConstantEmitter will
|
||||
// frequently return an empty Attribute, to signal we want to codegen
|
||||
// some trivial ctor calls and whatnots.
|
||||
constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(d);
|
||||
if (constant && !mlir::isa<cir::ZeroAttr>(constant) &&
|
||||
(trivialAutoVarInit !=
|
||||
LangOptions::TrivialAutoVarInitKind::Uninitialized)) {
|
||||
cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: constant aggregate");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(cir): In case we have a constant initializer, we can just emit a
|
||||
// store. But, in CIR, we wish to retain any ctor calls, so if it is a
|
||||
// CXX temporary object creation, we ensure the ctor call is used deferring
|
||||
// its removal/optimization to the CIR lowering.
|
||||
if (!constant || isa<CXXTemporaryObjectExpr>(init)) {
|
||||
initializeWhatIsTechnicallyUninitialized(addr);
|
||||
LValue lv = LValue::makeAddr(addr, type);
|
||||
emitExprAsInit(init, &d, lv);
|
||||
// In case lv has uses it means we indeed initialized something
|
||||
// out of it while trying to build the expression, mark it as such.
|
||||
mlir::Value val = lv.getAddress().getPointer();
|
||||
assert(val && "Should have an address");
|
||||
auto allocaOp = dyn_cast_or_null<cir::AllocaOp>(val.getDefiningOp());
|
||||
assert(allocaOp && "Address should come straight out of the alloca");
|
||||
|
||||
if (!allocaOp.use_empty())
|
||||
allocaOp.setInitAttr(mlir::UnitAttr::get(&getMLIRContext()));
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME(cir): migrate most of this file to use mlir::TypedAttr directly.
|
||||
auto typedConstant = mlir::dyn_cast<mlir::TypedAttr>(constant);
|
||||
assert(typedConstant && "expected typed attribute");
|
||||
if (!emission.IsConstantAggregate) {
|
||||
// For simple scalar/complex initialization, store the value directly.
|
||||
LValue lv = LValue::makeAddr(addr, type);
|
||||
assert(init && "expected initializer");
|
||||
mlir::Location initLoc = getLoc(init->getSourceRange());
|
||||
// lv.setNonGC(true);
|
||||
return emitStoreThroughLValue(
|
||||
RValue::get(builder.getConstant(initLoc, typedConstant)), lv);
|
||||
}
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitAutoVarCleanups(const clang::VarDecl &d) {
|
||||
void CIRGenFunction::emitAutoVarCleanups(
|
||||
const CIRGenFunction::AutoVarEmission &emission) {
|
||||
const VarDecl &d = *emission.Variable;
|
||||
|
||||
// Check the type for a cleanup.
|
||||
if (d.needsDestruction(getContext()))
|
||||
cgm.errorNYI(d.getSourceRange(), "emitAutoVarCleanups: type cleanup");
|
||||
@ -76,9 +191,9 @@ void CIRGenFunction::emitAutoVarCleanups(const clang::VarDecl &d) {
|
||||
/// register, or no storage class specifier. These turn into simple stack
|
||||
/// objects, globals depending on target.
|
||||
void CIRGenFunction::emitAutoVarDecl(const VarDecl &d) {
|
||||
emitAutoVarAlloca(d);
|
||||
emitAutoVarInit(d);
|
||||
emitAutoVarCleanups(d);
|
||||
CIRGenFunction::AutoVarEmission emission = emitAutoVarAlloca(d);
|
||||
emitAutoVarInit(emission);
|
||||
emitAutoVarCleanups(emission);
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitVarDecl(const VarDecl &d) {
|
||||
@ -94,10 +209,54 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) {
|
||||
|
||||
assert(d.hasLocalStorage());
|
||||
|
||||
assert(!cir::MissingFeatures::opAllocaVarDeclContext());
|
||||
CIRGenFunction::VarDeclContext varDeclCtx{*this, &d};
|
||||
return emitAutoVarDecl(d);
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitScalarInit(const Expr *init, mlir::Location loc,
|
||||
LValue lvalue, bool capturedByInit) {
|
||||
assert(!cir::MissingFeatures::objCLifetime());
|
||||
|
||||
SourceLocRAIIObject locRAII{*this, loc};
|
||||
mlir::Value value = emitScalarExpr(init);
|
||||
if (capturedByInit) {
|
||||
cgm.errorNYI(init->getSourceRange(), "emitScalarInit: captured by init");
|
||||
return;
|
||||
}
|
||||
assert(!cir::MissingFeatures::emitNullabilityCheck());
|
||||
emitStoreThroughLValue(RValue::get(value), lvalue, true);
|
||||
return;
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,
|
||||
LValue lvalue, bool capturedByInit) {
|
||||
SourceLocRAIIObject loc{*this, getLoc(init->getSourceRange())};
|
||||
if (capturedByInit) {
|
||||
cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: captured by init");
|
||||
return;
|
||||
}
|
||||
|
||||
QualType type = d->getType();
|
||||
|
||||
if (type->isReferenceType()) {
|
||||
cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: reference type");
|
||||
return;
|
||||
}
|
||||
switch (CIRGenFunction::getEvaluationKind(type)) {
|
||||
case cir::TEK_Scalar:
|
||||
emitScalarInit(init, getLoc(d->getSourceRange()), lvalue);
|
||||
return;
|
||||
case cir::TEK_Complex: {
|
||||
cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: complex type");
|
||||
return;
|
||||
}
|
||||
case cir::TEK_Aggregate:
|
||||
cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: aggregate type");
|
||||
return;
|
||||
}
|
||||
llvm_unreachable("bad evaluation kind");
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitDecl(const Decl &d) {
|
||||
switch (d.getKind()) {
|
||||
case Decl::Var: {
|
||||
|
@ -25,9 +25,77 @@ using namespace clang;
|
||||
using namespace clang::CIRGen;
|
||||
using namespace cir;
|
||||
|
||||
void CIRGenFunction::emitStoreThroughLValue(RValue src, LValue dst,
|
||||
bool isInit) {
|
||||
if (!dst.isSimple()) {
|
||||
cgm.errorNYI(dst.getPointer().getLoc(),
|
||||
"emitStoreThroughLValue: non-simple lvalue");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!cir::MissingFeatures::opLoadStoreObjC());
|
||||
|
||||
assert(src.isScalar() && "Can't emit an aggregate store with this method");
|
||||
emitStoreOfScalar(src.getScalarVal(), dst, isInit);
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
|
||||
bool isVolatile, QualType ty,
|
||||
bool isInit, bool isNontemporal) {
|
||||
assert(!cir::MissingFeatures::opLoadStoreThreadLocal());
|
||||
|
||||
if (ty->getAs<clang::VectorType>()) {
|
||||
cgm.errorNYI(addr.getPointer().getLoc(), "emitStoreOfScalar vector type");
|
||||
return;
|
||||
}
|
||||
|
||||
value = emitToMemory(value, ty);
|
||||
|
||||
assert(!cir::MissingFeatures::opLoadStoreAtomic());
|
||||
|
||||
// Update the alloca with more info on initialization.
|
||||
assert(addr.getPointer() && "expected pointer to exist");
|
||||
auto srcAlloca =
|
||||
dyn_cast_or_null<cir::AllocaOp>(addr.getPointer().getDefiningOp());
|
||||
if (currVarDecl && srcAlloca) {
|
||||
const VarDecl *vd = currVarDecl;
|
||||
assert(vd && "VarDecl expected");
|
||||
if (vd->hasInit())
|
||||
srcAlloca.setInitAttr(mlir::UnitAttr::get(&getMLIRContext()));
|
||||
}
|
||||
|
||||
assert(currSrcLoc && "must pass in source location");
|
||||
builder.createStore(*currSrcLoc, value, addr.getPointer() /*, isVolatile*/);
|
||||
|
||||
if (isNontemporal) {
|
||||
cgm.errorNYI(addr.getPointer().getLoc(), "emitStoreOfScalar nontemporal");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!cir::MissingFeatures::opTBAA());
|
||||
}
|
||||
|
||||
mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) {
|
||||
// Bool has a different representation in memory than in registers,
|
||||
// but in ClangIR, it is simply represented as a cir.bool value.
|
||||
// This function is here as a placeholder for possible future changes.
|
||||
return value;
|
||||
}
|
||||
|
||||
void CIRGenFunction::emitStoreOfScalar(mlir::Value value, LValue lvalue,
|
||||
bool isInit) {
|
||||
if (lvalue.getType()->isConstantMatrixType()) {
|
||||
assert(0 && "NYI: emitStoreOfScalar constant matrix type");
|
||||
return;
|
||||
}
|
||||
|
||||
emitStoreOfScalar(value, lvalue.getAddress(), lvalue.isVolatile(),
|
||||
lvalue.getType(), isInit, /*isNontemporal=*/false);
|
||||
}
|
||||
|
||||
mlir::Value CIRGenFunction::emitLoadOfScalar(LValue lvalue,
|
||||
SourceLocation loc) {
|
||||
assert(!cir::MissingFeatures::opLoadThreadLocal());
|
||||
assert(!cir::MissingFeatures::opLoadStoreThreadLocal());
|
||||
assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck());
|
||||
assert(!cir::MissingFeatures::opLoadBooleanRepresentation());
|
||||
|
||||
@ -115,7 +183,7 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
|
||||
builder.restoreInsertionPoint(builder.getBestAllocaInsertPoint(entryBlock));
|
||||
addr = builder.createAlloca(loc, /*addr type*/ localVarPtrTy,
|
||||
/*var type*/ ty, name, alignIntAttr);
|
||||
assert(!cir::MissingFeatures::opAllocaVarDeclContext());
|
||||
assert(!cir::MissingFeatures::astVarDeclInterface());
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
309
clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
Normal file
309
clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This contains code to emit Constant Expr nodes as LLVM code.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Address.h"
|
||||
#include "CIRGenConstantEmitter.h"
|
||||
#include "CIRGenFunction.h"
|
||||
#include "CIRGenModule.h"
|
||||
#include "mlir/IR/Attributes.h"
|
||||
#include "mlir/IR/BuiltinAttributeInterfaces.h"
|
||||
#include "mlir/IR/BuiltinAttributes.h"
|
||||
#include "clang/AST/APValue.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/OperationKinds.h"
|
||||
#include "clang/AST/RecordLayout.h"
|
||||
#include "clang/AST/StmtVisitor.h"
|
||||
#include "clang/Basic/Builtins.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
|
||||
#include "clang/CIR/Dialect/IR/CIRTypes.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/Sequence.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::CIRGen;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ConstExprEmitter
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// This class only needs to handle arrays, structs and unions.
|
||||
//
|
||||
// In LLVM codegen, when outside C++11 mode, those types are not constant
|
||||
// folded, while all other types are handled by constant folding.
|
||||
//
|
||||
// In CIR codegen, instead of folding things here, we should defer that work
|
||||
// to MLIR: do not attempt to do much here.
|
||||
class ConstExprEmitter
|
||||
: public StmtVisitor<ConstExprEmitter, mlir::Attribute, QualType> {
|
||||
CIRGenModule &cgm;
|
||||
LLVM_ATTRIBUTE_UNUSED ConstantEmitter &emitter;
|
||||
|
||||
public:
|
||||
ConstExprEmitter(ConstantEmitter &emitter)
|
||||
: cgm(emitter.cgm), emitter(emitter) {}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Visitor Methods
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
mlir::Attribute VisitStmt(Stmt *S, QualType T) { return {}; }
|
||||
|
||||
mlir::Attribute VisitConstantExpr(ConstantExpr *ce, QualType t) {
|
||||
if (mlir::Attribute result = emitter.tryEmitConstantExpr(ce))
|
||||
return result;
|
||||
return Visit(ce->getSubExpr(), t);
|
||||
}
|
||||
|
||||
mlir::Attribute VisitParenExpr(ParenExpr *pe, QualType t) {
|
||||
return Visit(pe->getSubExpr(), t);
|
||||
}
|
||||
|
||||
mlir::Attribute
|
||||
VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *pe,
|
||||
QualType t) {
|
||||
return Visit(pe->getReplacement(), t);
|
||||
}
|
||||
|
||||
mlir::Attribute VisitGenericSelectionExpr(GenericSelectionExpr *ge,
|
||||
QualType t) {
|
||||
return Visit(ge->getResultExpr(), t);
|
||||
}
|
||||
|
||||
mlir::Attribute VisitChooseExpr(ChooseExpr *ce, QualType t) {
|
||||
return Visit(ce->getChosenSubExpr(), t);
|
||||
}
|
||||
|
||||
mlir::Attribute VisitCompoundLiteralExpr(CompoundLiteralExpr *e, QualType t) {
|
||||
return Visit(e->getInitializer(), t);
|
||||
}
|
||||
|
||||
mlir::Attribute VisitCastExpr(CastExpr *e, QualType destType) {
|
||||
cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitCastExpr");
|
||||
return {};
|
||||
}
|
||||
|
||||
mlir::Attribute VisitCXXDefaultInitExpr(CXXDefaultInitExpr *die, QualType t) {
|
||||
cgm.errorNYI(die->getBeginLoc(),
|
||||
"ConstExprEmitter::VisitCXXDefaultInitExpr");
|
||||
return {};
|
||||
}
|
||||
|
||||
mlir::Attribute VisitExprWithCleanups(ExprWithCleanups *e, QualType t) {
|
||||
// Since this about constant emission no need to wrap this under a scope.
|
||||
return Visit(e->getSubExpr(), t);
|
||||
}
|
||||
|
||||
mlir::Attribute VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *e,
|
||||
QualType t) {
|
||||
return Visit(e->getSubExpr(), t);
|
||||
}
|
||||
|
||||
mlir::Attribute VisitImplicitValueInitExpr(ImplicitValueInitExpr *E,
|
||||
QualType T) {
|
||||
cgm.errorNYI(E->getBeginLoc(),
|
||||
"ConstExprEmitter::VisitImplicitValueInitExpr");
|
||||
return {};
|
||||
}
|
||||
|
||||
mlir::Attribute VisitInitListExpr(InitListExpr *ile, QualType t) {
|
||||
cgm.errorNYI(ile->getBeginLoc(), "ConstExprEmitter::VisitInitListExpr");
|
||||
return {};
|
||||
}
|
||||
|
||||
mlir::Attribute VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *e,
|
||||
QualType destType) {
|
||||
mlir::Attribute c = Visit(e->getBase(), destType);
|
||||
if (!c)
|
||||
return {};
|
||||
|
||||
cgm.errorNYI(e->getBeginLoc(),
|
||||
"ConstExprEmitter::VisitDesignatedInitUpdateExpr");
|
||||
return {};
|
||||
}
|
||||
|
||||
mlir::Attribute VisitCXXConstructExpr(CXXConstructExpr *e, QualType ty) {
|
||||
cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitCXXConstructExpr");
|
||||
return {};
|
||||
}
|
||||
|
||||
mlir::Attribute VisitStringLiteral(StringLiteral *e, QualType t) {
|
||||
cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitStringLiteral");
|
||||
return {};
|
||||
}
|
||||
|
||||
mlir::Attribute VisitObjCEncodeExpr(ObjCEncodeExpr *e, QualType t) {
|
||||
cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitObjCEncodeExpr");
|
||||
return {};
|
||||
}
|
||||
|
||||
mlir::Attribute VisitUnaryExtension(const UnaryOperator *e, QualType t) {
|
||||
return Visit(e->getSubExpr(), t);
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
mlir::Type convertType(QualType t) { return cgm.convertType(t); }
|
||||
};
|
||||
|
||||
// TODO(cir): this can be shared with LLVM's codegen
|
||||
static QualType getNonMemoryType(CIRGenModule &cgm, QualType type) {
|
||||
if (auto at = type->getAs<AtomicType>()) {
|
||||
return cgm.getASTContext().getQualifiedType(at->getValueType(),
|
||||
type.getQualifiers());
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ConstantEmitter
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
mlir::Attribute
|
||||
ConstantEmitter::tryEmitAbstractForInitializer(const VarDecl &d) {
|
||||
AbstractStateRAII state(*this, true);
|
||||
return tryEmitPrivateForVarInit(d);
|
||||
}
|
||||
|
||||
mlir::Attribute ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &d) {
|
||||
// Make a quick check if variable can be default NULL initialized
|
||||
// and avoid going through rest of code which may do, for c++11,
|
||||
// initialization of memory to all NULLs.
|
||||
if (!d.hasLocalStorage()) {
|
||||
QualType ty = cgm.getASTContext().getBaseElementType(d.getType());
|
||||
if (ty->isRecordType())
|
||||
if (d.getInit() && isa<CXXConstructExpr>(d.getInit())) {
|
||||
cgm.errorNYI(d.getInit()->getBeginLoc(),
|
||||
"tryEmitPrivateForVarInit CXXConstructExpr");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
inConstantContext = d.hasConstantInitialization();
|
||||
|
||||
const Expr *e = d.getInit();
|
||||
assert(e && "No initializer to emit");
|
||||
|
||||
QualType destType = d.getType();
|
||||
|
||||
if (!destType->isReferenceType()) {
|
||||
QualType nonMemoryDestType = getNonMemoryType(cgm, destType);
|
||||
if (mlir::Attribute c = ConstExprEmitter(*this).Visit(const_cast<Expr *>(e),
|
||||
nonMemoryDestType))
|
||||
return emitForMemory(c, destType);
|
||||
}
|
||||
|
||||
// Try to emit the initializer. Note that this can allow some things that
|
||||
// are not allowed by tryEmitPrivateForMemory alone.
|
||||
if (APValue *value = d.evaluateValue())
|
||||
return tryEmitPrivateForMemory(*value, destType);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
mlir::Attribute ConstantEmitter::tryEmitConstantExpr(const ConstantExpr *ce) {
|
||||
if (!ce->hasAPValueResult())
|
||||
return {};
|
||||
|
||||
QualType retType = ce->getType();
|
||||
if (ce->isGLValue())
|
||||
retType = cgm.getASTContext().getLValueReferenceType(retType);
|
||||
|
||||
return emitAbstract(ce->getBeginLoc(), ce->getAPValueResult(), retType);
|
||||
}
|
||||
|
||||
mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const APValue &value,
|
||||
QualType destType) {
|
||||
QualType nonMemoryDestType = getNonMemoryType(cgm, destType);
|
||||
mlir::Attribute c = tryEmitPrivate(value, nonMemoryDestType);
|
||||
return (c ? emitForMemory(c, destType) : nullptr);
|
||||
}
|
||||
|
||||
mlir::Attribute ConstantEmitter::emitAbstract(SourceLocation loc,
|
||||
const APValue &value,
|
||||
QualType destType) {
|
||||
AbstractStateRAII state(*this, true);
|
||||
mlir::Attribute c = tryEmitPrivate(value, destType);
|
||||
if (!c)
|
||||
cgm.errorNYI(loc, "emitAbstract failed, emit null constaant");
|
||||
return c;
|
||||
}
|
||||
|
||||
mlir::Attribute ConstantEmitter::emitForMemory(mlir::Attribute c,
|
||||
QualType destType) {
|
||||
// For an _Atomic-qualified constant, we may need to add tail padding.
|
||||
if (destType->getAs<AtomicType>()) {
|
||||
cgm.errorNYI("emitForMemory: atomic type");
|
||||
return {};
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
|
||||
QualType destType) {
|
||||
auto &builder = cgm.getBuilder();
|
||||
switch (value.getKind()) {
|
||||
case APValue::None:
|
||||
case APValue::Indeterminate:
|
||||
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate none or indeterminate");
|
||||
return {};
|
||||
case APValue::Int: {
|
||||
mlir::Type ty = cgm.convertType(destType);
|
||||
if (mlir::isa<cir::BoolType>(ty))
|
||||
return builder.getCIRBoolAttr(value.getInt().getZExtValue());
|
||||
assert(mlir::isa<cir::IntType>(ty) && "expected integral type");
|
||||
return cgm.getBuilder().getAttr<cir::IntAttr>(ty, value.getInt());
|
||||
}
|
||||
case APValue::Float: {
|
||||
const llvm::APFloat &init = value.getFloat();
|
||||
if (&init.getSemantics() == &llvm::APFloat::IEEEhalf() &&
|
||||
!cgm.getASTContext().getLangOpts().NativeHalfType &&
|
||||
cgm.getASTContext().getTargetInfo().useFP16ConversionIntrinsics()) {
|
||||
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate half");
|
||||
return {};
|
||||
} else {
|
||||
mlir::Type ty = cgm.convertType(destType);
|
||||
assert(mlir::isa<cir::CIRFPTypeInterface>(ty) &&
|
||||
"expected floating-point type");
|
||||
return cgm.getBuilder().getAttr<cir::FPAttr>(ty, init);
|
||||
}
|
||||
}
|
||||
case APValue::Array: {
|
||||
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate array");
|
||||
return {};
|
||||
}
|
||||
case APValue::Vector: {
|
||||
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate vector");
|
||||
return {};
|
||||
}
|
||||
case APValue::MemberPointer: {
|
||||
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate member pointer");
|
||||
return {};
|
||||
}
|
||||
case APValue::LValue:
|
||||
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate lvalue");
|
||||
return {};
|
||||
case APValue::Struct:
|
||||
case APValue::Union:
|
||||
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate struct or union");
|
||||
return {};
|
||||
case APValue::FixedPoint:
|
||||
case APValue::ComplexInt:
|
||||
case APValue::ComplexFloat:
|
||||
case APValue::AddrLabelDiff:
|
||||
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate fixed point, complex int, "
|
||||
"complex float, addr label diff");
|
||||
return {};
|
||||
}
|
||||
llvm_unreachable("Unknown APValue kind");
|
||||
}
|
@ -26,6 +26,7 @@
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/CIR/Dialect/IR/CIRDialect.h"
|
||||
#include "clang/CIR/MissingFeatures.h"
|
||||
#include "clang/CIR/TypeEvaluationKind.h"
|
||||
|
||||
#include "llvm/ADT/ScopedHashTable.h"
|
||||
@ -105,6 +106,27 @@ public:
|
||||
mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
|
||||
mlir::Location loc, clang::CharUnits alignment);
|
||||
|
||||
private:
|
||||
// Track current variable initialization (if there's one)
|
||||
const clang::VarDecl *currVarDecl = nullptr;
|
||||
class VarDeclContext {
|
||||
CIRGenFunction &p;
|
||||
const clang::VarDecl *oldVal = nullptr;
|
||||
|
||||
public:
|
||||
VarDeclContext(CIRGenFunction &p, const VarDecl *value) : p(p) {
|
||||
if (p.currVarDecl)
|
||||
oldVal = p.currVarDecl;
|
||||
p.currVarDecl = value;
|
||||
}
|
||||
|
||||
/// Can be used to restore the state early, before the dtor
|
||||
/// is run.
|
||||
void restore() { p.currVarDecl = oldVal; }
|
||||
~VarDeclContext() { restore(); }
|
||||
};
|
||||
|
||||
public:
|
||||
/// Use to track source locations across nested visitor traversals.
|
||||
/// Always use a `SourceLocRAIIObject` to change currSrcLoc.
|
||||
std::optional<mlir::Location> currSrcLoc;
|
||||
@ -170,16 +192,97 @@ public:
|
||||
|
||||
void emitDecl(const clang::Decl &d);
|
||||
|
||||
void emitScalarInit(const clang::Expr *init, mlir::Location loc,
|
||||
LValue lvalue, bool capturedByInit = false);
|
||||
|
||||
LValue emitDeclRefLValue(const clang::DeclRefExpr *e);
|
||||
|
||||
/// Determine whether the given initializer is trivial in the sense
|
||||
/// that it requires no code to be generated.
|
||||
bool isTrivialInitializer(const Expr *init);
|
||||
|
||||
/// Emit an expression as an initializer for an object (variable, field, etc.)
|
||||
/// at the given location. The expression is not necessarily the normal
|
||||
/// initializer for the object, and the address is not necessarily
|
||||
/// its normal location.
|
||||
///
|
||||
/// \param init the initializing expression
|
||||
/// \param d the object to act as if we're initializing
|
||||
/// \param lvalue the lvalue to initialize
|
||||
/// \param capturedByInit true if \p d is a __block variable whose address is
|
||||
/// potentially changed by the initializer
|
||||
void emitExprAsInit(const clang::Expr *init, const clang::ValueDecl *d,
|
||||
LValue lvalue, bool capturedByInit = false);
|
||||
|
||||
/// Emit code and set up symbol table for a variable declaration with auto,
|
||||
/// register, or no storage class specifier. These turn into simple stack
|
||||
/// objects, globals depending on target.
|
||||
void emitAutoVarDecl(const clang::VarDecl &d);
|
||||
|
||||
void emitAutoVarAlloca(const clang::VarDecl &d);
|
||||
void emitAutoVarInit(const clang::VarDecl &d);
|
||||
void emitAutoVarCleanups(const clang::VarDecl &d);
|
||||
struct AutoVarEmission {
|
||||
const clang::VarDecl *Variable;
|
||||
/// The address of the alloca for languages with explicit address space
|
||||
/// (e.g. OpenCL) or alloca casted to generic pointer for address space
|
||||
/// agnostic languages (e.g. C++). Invalid if the variable was emitted
|
||||
/// as a global constant.
|
||||
Address Addr;
|
||||
|
||||
/// True if the variable is of aggregate type and has a constant
|
||||
/// initializer.
|
||||
bool IsConstantAggregate = false;
|
||||
|
||||
/// True if the variable is a __block variable that is captured by an
|
||||
/// escaping block.
|
||||
bool IsEscapingByRef = false;
|
||||
|
||||
mlir::Value NRVOFlag{};
|
||||
|
||||
struct Invalid {};
|
||||
AutoVarEmission(Invalid) : Variable(nullptr), Addr(Address::invalid()) {}
|
||||
|
||||
AutoVarEmission(const clang::VarDecl &variable)
|
||||
: Variable(&variable), Addr(Address::invalid()) {}
|
||||
|
||||
static AutoVarEmission invalid() { return AutoVarEmission(Invalid()); }
|
||||
|
||||
bool wasEmittedAsGlobal() const { return !Addr.isValid(); }
|
||||
|
||||
/// Returns the raw, allocated address, which is not necessarily
|
||||
/// the address of the object itself. It is casted to default
|
||||
/// address space for address space agnostic languages.
|
||||
Address getAllocatedAddress() const { return Addr; }
|
||||
|
||||
/// Returns the address of the object within this declaration.
|
||||
/// Note that this does not chase the forwarding pointer for
|
||||
/// __block decls.
|
||||
Address getObjectAddress(CIRGenFunction &CGF) const {
|
||||
if (!IsEscapingByRef)
|
||||
return Addr;
|
||||
|
||||
assert(!cir::MissingFeatures::opAllocaEscapeByReference());
|
||||
return Address::invalid();
|
||||
}
|
||||
};
|
||||
|
||||
AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d);
|
||||
void emitAutoVarInit(const AutoVarEmission &emission);
|
||||
void emitAutoVarCleanups(const AutoVarEmission &emission);
|
||||
|
||||
void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile,
|
||||
clang::QualType ty, bool isInit = false,
|
||||
bool isNontemporal = false);
|
||||
void emitStoreOfScalar(mlir::Value value, LValue lvalue, bool isInit);
|
||||
|
||||
/// Given a value and its clang type, returns the value casted to its memory
|
||||
/// representation.
|
||||
/// Note: CIR defers most of the special casting to the final lowering passes
|
||||
/// to conserve the high level information.
|
||||
mlir::Value emitToMemory(mlir::Value Value, clang::QualType Ty);
|
||||
|
||||
/// Store the specified rvalue into the specified
|
||||
/// lvalue, where both are guaranteed to the have the same type, and that type
|
||||
/// is 'Ty'.
|
||||
void emitStoreThroughLValue(RValue Src, LValue Dst, bool isInit = false);
|
||||
|
||||
/// This method handles emission of any variable declaration
|
||||
/// inside a function, including static vars etc.
|
||||
|
@ -147,6 +147,20 @@ public:
|
||||
return diags.Report(loc, diagID) << feature << name;
|
||||
}
|
||||
|
||||
DiagnosticBuilder errorNYI(mlir::Location loc, llvm::StringRef feature) {
|
||||
// TODO: Convert the location to a SourceLocation
|
||||
unsigned diagID = diags.getCustomDiagID(
|
||||
DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0");
|
||||
return diags.Report(diagID) << feature;
|
||||
}
|
||||
|
||||
DiagnosticBuilder errorNYI(llvm::StringRef feature) {
|
||||
// TODO: Make a default location? currSrcLoc?
|
||||
unsigned diagID = diags.getCustomDiagID(
|
||||
DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0");
|
||||
return diags.Report(diagID) << feature;
|
||||
}
|
||||
|
||||
DiagnosticBuilder errorNYI(SourceRange, llvm::StringRef);
|
||||
|
||||
template <typename T>
|
||||
|
@ -10,6 +10,7 @@ add_clang_library(clangCIR
|
||||
CIRGenerator.cpp
|
||||
CIRGenDecl.cpp
|
||||
CIRGenExpr.cpp
|
||||
CIRGenExprConstant.cpp
|
||||
CIRGenExprScalar.cpp
|
||||
CIRGenFunction.cpp
|
||||
CIRGenModule.cpp
|
||||
|
@ -1,9 +1,4 @@
|
||||
// RUN: not %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s
|
||||
|
||||
// This error is caused by the "const int i = 2" line in f2(). When
|
||||
// initaliziers are implemented, the checks there should be updated
|
||||
// and the "not" should be removed from the run line.
|
||||
// CHECK: error: ClangIR code gen Not Yet Implemented: emitAutoVarInit
|
||||
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s
|
||||
|
||||
int f1() {
|
||||
int i;
|
||||
@ -22,13 +17,15 @@ int f2() {
|
||||
}
|
||||
|
||||
// CHECK: cir.func @f2() -> !cir.int<s, 32>
|
||||
// CHECK: %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", const] {alignment = 4 : i64}
|
||||
// CHECK: %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init, const] {alignment = 4 : i64}
|
||||
// CHECK: %[[TWO:.*]] = cir.const #cir.int<2> : !cir.int<s, 32>
|
||||
// CHECK: cir.store %[[TWO]], %[[I_PTR]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
|
||||
// CHECK: %[[I:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
|
||||
// CHECK: cir.return %[[I]] : !cir.int<s, 32>
|
||||
|
||||
int f3(int i) {
|
||||
return i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// CHECK: cir.func @f3(%[[ARG:.*]]: !cir.int<s, 32> loc({{.*}})) -> !cir.int<s, 32>
|
||||
// CHECK: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init] {alignment = 4 : i64}
|
||||
|
65
clang/test/CIR/CodeGen/local-vars.cpp
Normal file
65
clang/test/CIR/CodeGen/local-vars.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s
|
||||
|
||||
void test() {
|
||||
int i = 1;
|
||||
long l = 2l;
|
||||
float f = 3.0f;
|
||||
double d = 4.0;
|
||||
bool b1 = true;
|
||||
bool b2 = false;
|
||||
const int ci = 1;
|
||||
const long cl = 2l;
|
||||
const float cf = 3.0f;
|
||||
const double cd = 4.0;
|
||||
const bool cb1 = true;
|
||||
const bool cb2 = false;
|
||||
int uii;
|
||||
long uil;
|
||||
float uif;
|
||||
double uid;
|
||||
bool uib;
|
||||
}
|
||||
|
||||
// CHECK: module
|
||||
// CHECK: cir.func @test()
|
||||
// CHECK: %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init] {alignment = 4 : i64}
|
||||
// CHECK: %[[L_PTR:.*]] = cir.alloca !cir.int<s, 64>, !cir.ptr<!cir.int<s, 64>>, ["l", init] {alignment = 8 : i64}
|
||||
// CHECK: %[[F_PTR:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f", init] {alignment = 4 : i64}
|
||||
// CHECK: %[[D_PTR:.*]] = cir.alloca !cir.double, !cir.ptr<!cir.double>, ["d", init] {alignment = 8 : i64}
|
||||
// CHECK: %[[B1_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b1", init] {alignment = 1 : i64}
|
||||
// CHECK: %[[B2_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b2", init] {alignment = 1 : i64}
|
||||
// CHECK: %[[CI_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["ci", init, const] {alignment = 4 : i64}
|
||||
// CHECK: %[[CL_PTR:.*]] = cir.alloca !cir.int<s, 64>, !cir.ptr<!cir.int<s, 64>>, ["cl", init, const] {alignment = 8 : i64}
|
||||
// CHECK: %[[CF_PTR:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["cf", init, const] {alignment = 4 : i64}
|
||||
// CHECK: %[[CD_PTR:.*]] = cir.alloca !cir.double, !cir.ptr<!cir.double>, ["cd", init, const] {alignment = 8 : i64}
|
||||
// CHECK: %[[CB1_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cb1", init, const] {alignment = 1 : i64}
|
||||
// CHECK: %[[CB2_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cb2", init, const] {alignment = 1 : i64}
|
||||
// CHECK: %[[UII_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["uii"] {alignment = 4 : i64}
|
||||
// CHECK: %[[UIL_PTR:.*]] = cir.alloca !cir.int<s, 64>, !cir.ptr<!cir.int<s, 64>>, ["uil"] {alignment = 8 : i64}
|
||||
// CHECK: %[[UIF_PTR:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["uif"] {alignment = 4 : i64}
|
||||
// CHECK: %[[UID_PTR:.*]] = cir.alloca !cir.double, !cir.ptr<!cir.double>, ["uid"] {alignment = 8 : i64}
|
||||
// CHECK: %[[UIB_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["uib"] {alignment = 1 : i64}
|
||||
// CHECK: %[[ONE:.*]] = cir.const #cir.int<1> : !cir.int<s, 32>
|
||||
// CHECK: cir.store %[[ONE]], %[[I_PTR]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
|
||||
// CHECK: %[[TWO:.*]] = cir.const #cir.int<2> : !cir.int<s, 64>
|
||||
// CHECK: cir.store %[[TWO]], %[[L_PTR]] : !cir.int<s, 64>, !cir.ptr<!cir.int<s, 64>>
|
||||
// CHECK: %[[THREE:.*]] = cir.const #cir.fp<3.0{{.*}}> : !cir.float
|
||||
// CHECK: cir.store %[[THREE]], %[[F_PTR]] : !cir.float, !cir.ptr<!cir.float>
|
||||
// CHECK: %[[FOUR:.*]] = cir.const #cir.fp<4.0{{.*}}> : !cir.double
|
||||
// CHECK: cir.store %[[FOUR]], %[[D_PTR]] : !cir.double, !cir.ptr<!cir.double>
|
||||
// CHECK: %[[TRUE:.*]] = cir.const #true
|
||||
// CHECK: cir.store %[[TRUE]], %[[B1_PTR]] : !cir.bool, !cir.ptr<!cir.bool>
|
||||
// CHECK: %[[FALSE:.*]] = cir.const #false
|
||||
// CHECK: cir.store %[[FALSE]], %[[B2_PTR]] : !cir.bool, !cir.ptr<!cir.bool>
|
||||
// CHECK: %[[ONEC:.*]] = cir.const #cir.int<1> : !cir.int<s, 32>
|
||||
// CHECK: cir.store %[[ONEC]], %[[CI_PTR]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
|
||||
// CHECK: %[[TWOC:.*]] = cir.const #cir.int<2> : !cir.int<s, 64>
|
||||
// CHECK: cir.store %[[TWOC]], %[[CL_PTR]] : !cir.int<s, 64>, !cir.ptr<!cir.int<s, 64>>
|
||||
// CHECK: %[[THREEC:.*]] = cir.const #cir.fp<3.0{{.*}}> : !cir.float
|
||||
// CHECK: cir.store %[[THREEC]], %[[CF_PTR]] : !cir.float, !cir.ptr<!cir.float>
|
||||
// CHECK: %[[FOURC:.*]] = cir.const #cir.fp<4.0{{.*}}> : !cir.double
|
||||
// CHECK: cir.store %[[FOURC]], %[[CD_PTR]] : !cir.double, !cir.ptr<!cir.double>
|
||||
// CHECK: %[[TRUEC:.*]] = cir.const #true
|
||||
// CHECK: cir.store %[[TRUEC]], %[[CB1_PTR]] : !cir.bool, !cir.ptr<!cir.bool>
|
||||
// CHECK: %[[FALSEC:.*]] = cir.const #false
|
||||
// CHECK: cir.store %[[FALSEC]], %[[CB2_PTR]] : !cir.bool, !cir.ptr<!cir.bool>
|
@ -1,9 +1,4 @@
|
||||
// RUN: not %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - 2>&1 | FileCheck %s
|
||||
|
||||
// This error is caused by the "const int i = 2" line in f2(). When
|
||||
// initaliziers are implemented, the checks there should be updated
|
||||
// and the "not" should be removed from the run line.
|
||||
// CHECK: error: ClangIR code gen Not Yet Implemented: emitAutoVarInit
|
||||
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - 2>&1 | FileCheck %s
|
||||
|
||||
int f1() {
|
||||
int i;
|
||||
@ -22,6 +17,7 @@ int f2() {
|
||||
|
||||
// CHECK: define{{.*}} i32 @f2() {
|
||||
// CHECK: %[[I_PTR:.*]] = alloca i32, i64 1, align 4
|
||||
// CHECK: store i32 2, ptr %[[I_PTR]], align 4
|
||||
// CHECK: %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4
|
||||
// CHECK: ret i32 %[[I]]
|
||||
|
||||
|
55
clang/test/CIR/Lowering/local-vars.cpp
Normal file
55
clang/test/CIR/Lowering/local-vars.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - 2>&1 | FileCheck %s
|
||||
|
||||
void test() {
|
||||
int i = 1;
|
||||
long l = 2l;
|
||||
float f = 3.0f;
|
||||
double d = 4.0;
|
||||
bool b1 = true;
|
||||
bool b2 = false;
|
||||
const int ci = 1;
|
||||
const long cl = 2l;
|
||||
const float cf = 3.0f;
|
||||
const double cd = 4.0;
|
||||
const bool cb1 = true;
|
||||
const bool cb2 = false;
|
||||
int uii;
|
||||
long uil;
|
||||
float uif;
|
||||
double uid;
|
||||
bool uib;
|
||||
}
|
||||
|
||||
// Note: The alignment of i64 stores below is wrong. That should be fixed
|
||||
// when we add alignment attributes to the load/store ops.
|
||||
|
||||
// CHECK: define{{.*}} void @test()
|
||||
// CHECK: %[[I_PTR:.*]] = alloca i32, i64 1, align 4
|
||||
// CHECK: %[[L_PTR:.*]] = alloca i64, i64 1, align 8
|
||||
// CHECK: %[[F_PTR:.*]] = alloca float, i64 1, align 4
|
||||
// CHECK: %[[D_PTR:.*]] = alloca double, i64 1, align 8
|
||||
// CHECK: %[[B1_PTR:.*]] = alloca i8, i64 1, align 1
|
||||
// CHECK: %[[B2_PTR:.*]] = alloca i8, i64 1, align 1
|
||||
// CHECK: %[[CI_PTR:.*]] = alloca i32, i64 1, align 4
|
||||
// CHECK: %[[CL_PTR:.*]] = alloca i64, i64 1, align 8
|
||||
// CHECK: %[[CF_PTR:.*]] = alloca float, i64 1, align 4
|
||||
// CHECK: %[[CD_PTR:.*]] = alloca double, i64 1, align 8
|
||||
// CHECK: %[[CB1_PTR:.*]] = alloca i8, i64 1, align 1
|
||||
// CHECK: %[[CB2_PTR:.*]] = alloca i8, i64 1, align 1
|
||||
// CHECK: %[[UII_PTR:.*]] = alloca i32, i64 1, align 4
|
||||
// CHECK: %[[UIL_PTR:.*]] = alloca i64, i64 1, align 8
|
||||
// CHECK: %[[UIF_PTR:.*]] = alloca float, i64 1, align 4
|
||||
// CHECK: %[[UID_PTR:.*]] = alloca double, i64 1, align 8
|
||||
// CHECK: %[[UIB_PTR:.*]] = alloca i8, i64 1, align 1
|
||||
// CHECK: store i32 1, ptr %[[I_PTR]], align 4
|
||||
// CHECK: store i64 2, ptr %[[L_PTR]], align 4
|
||||
// CHECK: store float 3.000000e+00, ptr %[[F_PTR]], align 4
|
||||
// CHECK: store double 4.000000e+00, ptr %[[D_PTR]], align 8
|
||||
// CHECK: store i8 1, ptr %[[B1_PTR]], align 1
|
||||
// CHECK: store i8 0, ptr %[[B2_PTR]], align 1
|
||||
// CHECK: store i32 1, ptr %[[CI_PTR]], align 4
|
||||
// CHECK: store i64 2, ptr %[[CL_PTR]], align 4
|
||||
// CHECK: store float 3.000000e+00, ptr %[[CF_PTR]], align 4
|
||||
// CHECK: store double 4.000000e+00, ptr %[[CD_PTR]], align 8
|
||||
// CHECK: store i8 1, ptr %[[CB1_PTR]], align 1
|
||||
// CHECK: store i8 0, ptr %[[CB2_PTR]], align 1
|
Loading…
x
Reference in New Issue
Block a user