Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,48 @@ def FPAttr : CIR_Attr<"FP", "fp", [TypedAttrInterface]> {
}];
}


//===----------------------------------------------------------------------===//
// ConstArrayAttr
//===----------------------------------------------------------------------===//

def ConstArrayAttr : CIR_Attr<"ConstArray", "const_array", [TypedAttrInterface]> {
let summary = "A constant array from ArrayAttr or StringRefAttr";
let description = [{
An CIR array attribute is an array of literals of the specified attr types.
}];

let parameters = (ins AttributeSelfTypeParameter<"">:$type,
"mlir::Attribute":$elts,
"int":$trailingZerosNum);

// Define a custom builder for the type; that removes the need to pass
// in an MLIRContext instance, as it can be infered from the `type`.
let builders = [
AttrBuilderWithInferredContext<(ins "cir::ArrayType":$type,
"mlir::Attribute":$elts), [{
int zeros = 0;
auto typeSize = mlir::cast<cir::ArrayType>(type).getSize();
if (auto str = mlir::dyn_cast<mlir::StringAttr>(elts))
zeros = typeSize - str.size();
else
zeros = typeSize - mlir::cast<mlir::ArrayAttr>(elts).size();

return $_get(type.getContext(), type, elts, zeros);
}]>
];

// Printing and parsing available in CIRDialect.cpp
let hasCustomAssemblyFormat = 1;

// Enable verifier.
let genVerifyDecl = 1;

let extraClassDeclaration = [{
bool hasTrailingZeros() const { return getTrailingZerosNum() != 0; };
}];
}

//===----------------------------------------------------------------------===//
// ConstPtrAttr
//===----------------------------------------------------------------------===//
Expand Down
35 changes: 35 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,41 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return false;
}

// Return true if this is the null value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't quite the comment I wanted to see. It doesn't tell me any more than the function name did. It needs to explain what "null value" means in the context of this function. Specifically, it doesn't mean "is equal to zero" because -0.0 is equal to zero. Strings also get special handling, as will structures when they are implemented. Please describe the purpose of the function and highlight the special cases.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, I updated the comment to describe the implemented cases

bool isNullValue(mlir::Attribute attr) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be useful to have a comment here explaining the intent of this function. In particular, some clarification is necessary as to what it means for an FPAtt. The function returns false for -0.0, which means it is not simply testing for equality with zero.

if (mlir::isa<cir::ZeroAttr>(attr))
return true;

if (const auto ptrVal = mlir::dyn_cast<cir::ConstPtrAttr>(attr))
return ptrVal.isNullValue();

if (const auto intVal = mlir::dyn_cast<cir::IntAttr>(attr))
return intVal.isNullValue();

if (const auto boolVal = mlir::dyn_cast<cir::BoolAttr>(attr))
return !boolVal.getValue();

if (auto fpAttr = mlir::dyn_cast<cir::FPAttr>(attr)) {
auto fpVal = fpAttr.getValue();
bool ignored;
llvm::APFloat fv(+0.0);
fv.convert(fpVal.getSemantics(), llvm::APFloat::rmNearestTiesToEven,
&ignored);
return fv.bitwiseIsEqual(fpVal);
}

if (const auto arrayVal = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
if (mlir::isa<mlir::StringAttr>(arrayVal.getElts()))
return false;
for (const auto elt : mlir::cast<mlir::ArrayAttr>(arrayVal.getElts())) {
if (!isNullValue(elt))
return false;
}
return true;
}
return false;
}

bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); }

// Creates constant nullptr for pointer type ty.
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

#include "CIRGenFunction.h"
#include "CIRGenModule.h"
#include "llvm/ADT/SmallVector.h"

namespace clang::CIRGen {

Expand All @@ -41,6 +40,9 @@ class ConstantEmitter {
/// block addresses or PredefinedExprs.
ConstantEmitter(CIRGenFunction &cgf) : cgm(cgf.cgm), cgf(&cgf) {}

ConstantEmitter(CIRGenModule &cgm, CIRGenFunction *cgf = nullptr)
: cgm(cgm), cgf(cgf) {}

ConstantEmitter(const ConstantEmitter &other) = delete;
ConstantEmitter &operator=(const ConstantEmitter &other) = delete;

Expand All @@ -66,7 +68,7 @@ class ConstantEmitter {
mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value,
QualType t);

mlir::Attribute tryEmitConstantExpr(const ConstantExpr *CE);
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
Expand Down
1 change: 0 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ void CIRGenFunction::emitScalarInit(const Expr *init, mlir::Location loc,
}
assert(!cir::MissingFeatures::emitNullabilityCheck());
emitStoreThroughLValue(RValue::get(value), lvalue, true);
return;
}

void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,
Expand Down
122 changes: 112 additions & 10 deletions clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,58 @@ class ConstExprEmitter

// TODO(cir): this can be shared with LLVM's codegen
static QualType getNonMemoryType(CIRGenModule &cgm, QualType type) {
if (auto at = type->getAs<AtomicType>()) {
if (const auto *at = type->getAs<AtomicType>()) {
return cgm.getASTContext().getQualifiedType(at->getValueType(),
type.getQualifiers());
}
return type;
}

static mlir::Attribute
emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType,
mlir::Type commonElementType, unsigned arrayBound,
SmallVectorImpl<mlir::TypedAttr> &elements,
mlir::TypedAttr filler) {
const CIRGenBuilderTy &builder = cgm.getBuilder();

unsigned nonzeroLength = arrayBound;
if (elements.size() < nonzeroLength && builder.isNullValue(filler))
nonzeroLength = elements.size();

if (nonzeroLength == elements.size()) {
while (nonzeroLength > 0 &&
builder.isNullValue(elements[nonzeroLength - 1]))
--nonzeroLength;
}

if (nonzeroLength == 0)
return cir::ZeroAttr::get(builder.getContext(), desiredType);

const unsigned trailingZeroes = arrayBound - nonzeroLength;

// Add a zeroinitializer array filler if we have lots of trailing zeroes.
if (trailingZeroes >= 8) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've dropped a comment from the incubator code here that explains the purpose of the comparison with 8 -- "if we have lots of trailing zeroes". Without the comment, 8 is purely a magic number with no context.

assert(elements.size() >= nonzeroLength &&
"missing initializer for non-zero element");
} else if (elements.size() != arrayBound) {
elements.resize(arrayBound, filler);

if (filler.getType() != commonElementType)
cgm.errorNYI(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this NYI? The error message here is not correct.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is that it's will not be assert or error after we support StructType

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. It confused me because you put the errorNYI here rather than at the bottom of the function where the incubator code creates the struct. I suppose this way would work, but it will be easier to update the code later if you maintain the structure of the incubator code and put the errorNYI code at the end.

"array filter type should always be the same as element type");
}

SmallVector<mlir::Attribute, 4> eles;
eles.reserve(elements.size());

for (const auto &element : elements)
eles.push_back(element);

return cir::ConstArrayAttr::get(
cir::ArrayType::get(builder.getContext(), commonElementType, arrayBound),
mlir::ArrayAttr::get(builder.getContext(), eles));
}

//===----------------------------------------------------------------------===//
// ConstantEmitter
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -271,16 +316,59 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
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);
}

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 {};
const ArrayType *arrayTy = cgm.getASTContext().getAsArrayType(destType);
const QualType arrayElementTy = arrayTy->getElementType();
const unsigned numElements = value.getArraySize();
const unsigned numInitElts = value.getArrayInitializedElts();

mlir::Attribute filler;
if (value.hasArrayFiller()) {
filler = tryEmitPrivate(value.getArrayFiller(), arrayElementTy);
if (!filler)
return {};
}

SmallVector<mlir::TypedAttr, 16> elements;
if (filler && builder.isNullValue(filler))
elements.reserve(numInitElts + 1);
else
elements.reserve(numInitElts);

mlir::Type commonElementType;
for (unsigned i = 0; i < numInitElts; ++i) {
const APValue &arrayElement = value.getArrayInitializedElt(i);
const mlir::Attribute element =
tryEmitPrivateForMemory(arrayElement, arrayElementTy);
if (!element)
return {};

const mlir::TypedAttr elementTyped = mlir::cast<mlir::TypedAttr>(element);
if (i == 0)
commonElementType = elementTyped.getType();
else if (elementTyped.getType() != commonElementType) {
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate Array without common "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I'm not sure why you need NYI here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is that it's will not be assert or error after we support StructType

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you just set commonElementType to {} as is done in the incubator, this will eventually reach the NYI state in emitConstantArray and this code won't need to be updated later.

"element type");
return {};
}

elements.push_back(elementTyped);
}

mlir::TypedAttr typedFiller = llvm::cast_or_null<mlir::TypedAttr>(filler);
if (filler && !typedFiller)
cgm.errorNYI("array filler should always be typed");

mlir::Type desiredType = cgm.convertType(destType);
return emitArrayConstant(cgm, desiredType, commonElementType, numElements,
elements, typedFiller);
}
case APValue::Vector: {
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate vector");
Expand All @@ -290,9 +378,23 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate member pointer");
return {};
}
case APValue::LValue:
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate lvalue");
case APValue::LValue: {

if (value.getLValueBase()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're probably going to want to leave ConstantLValueEmitter::tryEmit() as a separate function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, i think later we need to upstreaming ConstantLValueEmitter and also implement the visitors

cgm.errorNYI("non-null pointer initialization");
} else {

mlir::Type desiredType = cgm.convertType(destType);
if (const cir::PointerType ptrType =
mlir::dyn_cast<cir::PointerType>(desiredType)) {
return builder.getConstPtrAttr(ptrType,
value.getLValueOffset().getQuantity());
} else {
llvm_unreachable("non-pointer variable initialized with a pointer");
}
}
return {};
}
case APValue::Struct:
case APValue::Union:
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate struct or union");
Expand Down
38 changes: 5 additions & 33 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "CIRGenModule.h"
#include "CIRGenConstantEmitter.h"
#include "CIRGenFunction.h"

#include "clang/AST/ASTContext.h"
Expand Down Expand Up @@ -127,7 +128,8 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,

void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
bool isTentative) {
mlir::Type type = convertType(vd->getType());
const QualType astTy = vd->getType();
const mlir::Type type = convertType(vd->getType());
if (clang::IdentifierInfo *identifier = vd->getIdentifier()) {
auto varOp = builder.create<cir::GlobalOp>(getLoc(vd->getSourceRange()),
identifier->getName(), type);
Expand All @@ -140,38 +142,8 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
if (initExpr) {
mlir::Attribute initializer;
if (APValue *value = initDecl->evaluateValue()) {
switch (value->getKind()) {
case APValue::Int: {
if (mlir::isa<cir::BoolType>(type))
initializer =
builder.getCIRBoolAttr(value->getInt().getZExtValue());
else
initializer = builder.getAttr<cir::IntAttr>(type, value->getInt());
break;
}
case APValue::Float: {
initializer = builder.getAttr<cir::FPAttr>(type, value->getFloat());
break;
}
case APValue::LValue: {
if (value->getLValueBase()) {
errorNYI(initExpr->getSourceRange(),
"non-null pointer initialization");
} else {
if (auto ptrType = mlir::dyn_cast<cir::PointerType>(type)) {
initializer = builder.getConstPtrAttr(
ptrType, value->getLValueOffset().getQuantity());
} else {
llvm_unreachable(
"non-pointer variable initialized with a pointer");
}
}
break;
}
default:
errorNYI(initExpr->getSourceRange(), "unsupported initializer kind");
break;
}
ConstantEmitter emitter(*this);
initializer = emitter.tryEmitPrivateForMemory(*value, astTy);
} else {
errorNYI(initExpr->getSourceRange(), "non-constant initializer");
}
Expand Down
Loading