Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/Support/ErrorHandling.h"

Expand Down Expand Up @@ -177,6 +178,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return create<cir::AllocaOp>(loc, addrType, type, name, alignment);
}

mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global) {
assert(!cir::MissingFeatures::addressSpace());
return create<cir::GetGlobalOp>(loc, getPointerTo(global.getSymType()),
global.getSymName());
}

cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr,
bool isVolatile = false, uint64_t alignment = 0) {
mlir::IntegerAttr intAttr;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct MissingFeatures {
static bool opGlobalWeakRef() { return false; }
static bool opGlobalLinkage() { return false; }
static bool opGlobalSetVisitibility() { return false; }
static bool opGlobalUnnamedAddr() { return false; }

static bool supportIFuncAttr() { return false; }
static bool supportVisibility() { return false; }
Expand Down
28 changes: 28 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,34 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
CIRGenBuilderTy(mlir::MLIRContext &mlirContext, const CIRGenTypeCache &tc)
: CIRBaseBuilderTy(mlirContext), typeCache(tc) {}

/// Get a cir::ConstArrayAttr for a string literal.
/// Note: This is different from what is returned by
/// mlir::Builder::getStringAttr() which is an mlir::StringAttr.
mlir::Attribute getString(llvm::StringRef str, mlir::Type eltTy,
std::optional<size_t> size) {
size_t finalSize = size.value_or(str.size());

size_t lastNonZeroPos = str.find_last_not_of('\0');
// If the string is full of null bytes, emit a #cir.zero rather than
// a #cir.const_array.
if (lastNonZeroPos == llvm::StringRef::npos) {
auto arrayTy = cir::ArrayType::get(eltTy, finalSize);
return cir::ZeroAttr::get(arrayTy);
}
// We emit trailing zeros only if there are multiple trailing zeros.
size_t trailingZerosNum = 0;
if (finalSize > lastNonZeroPos + 2)
trailingZerosNum = finalSize - lastNonZeroPos - 1;
auto truncatedArrayTy =
cir::ArrayType::get(eltTy, finalSize - trailingZerosNum);
auto fullArrayTy = cir::ArrayType::get(eltTy, finalSize);
return cir::ConstArrayAttr::get(
fullArrayTy,
mlir::StringAttr::get(str.drop_back(trailingZerosNum),
truncatedArrayTy),
trailingZerosNum);
}

std::string getUniqueAnonRecordName() { return getUniqueRecordName("anon"); }

std::string getUniqueRecordName(const std::string &baseName) {
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,16 @@ CIRGenFunction::emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e) {
return lv;
}

LValue CIRGenFunction::emitStringLiteralLValue(const StringLiteral *e) {
cir::GlobalOp globalOp = cgm.getGlobalForStringLiteral(e);
assert(!cir::MissingFeatures::opGlobalAlignment());
mlir::Value addr =
builder.createGetGlobal(getLoc(e->getSourceRange()), globalOp);
return makeAddrLValue(
Address(addr, globalOp.getSymType(), CharUnits::fromQuantity(1)),
e->getType(), AlignmentSource::Decl);
}

/// Casts are never lvalues unless that cast is to a reference type. If the cast
/// is to a reference, we can have the usual lvalue result, otherwise if a cast
/// is needed by the code generator in an lvalue context, then it must mean that
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
return emitArraySubscriptExpr(cast<ArraySubscriptExpr>(e));
case Expr::UnaryOperatorClass:
return emitUnaryOpLValue(cast<UnaryOperator>(e));
case Expr::StringLiteralClass:
return emitStringLiteralLValue(cast<StringLiteral>(e));
case Expr::MemberExprClass:
return emitMemberExpr(cast<MemberExpr>(e));
case Expr::BinaryOperatorClass:
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,8 @@ class CIRGenFunction : public CIRGenTypeCache {

mlir::Value emitStoreThroughBitfieldLValue(RValue src, LValue dstresult);

LValue emitStringLiteralLValue(const StringLiteral *e);

mlir::LogicalResult emitSwitchBody(const clang::Stmt *s);
mlir::LogicalResult emitSwitchCase(const clang::SwitchCase &s,
bool buildingTopLevelCase);
Expand Down
102 changes: 102 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,30 @@ void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd,
llvm_unreachable("Invalid argument to CIRGenModule::emitGlobalDefinition");
}

mlir::Attribute
CIRGenModule::getConstantArrayFromStringLiteral(const StringLiteral *e) {
assert(!e->getType()->isPointerType() && "Strings are always arrays");

// Don't emit it as the address of the string, emit the string data itself
// as an inline array.
if (e->getCharByteWidth() == 1) {
SmallString<64> str(e->getString());

// Resize the string to the right size, which is indicated by its type.
const ConstantArrayType *cat =
astContext.getAsConstantArrayType(e->getType());
uint64_t finalSize = cat->getZExtSize();
str.resize(finalSize);

mlir::Type eltTy = convertType(cat->getElementType());
return builder.getString(str, eltTy, finalSize);
}

errorNYI(e->getSourceRange(),
"getConstantArrayFromStringLiteral: wide characters");
return mlir::Attribute();
}

static bool shouldBeInCOMDAT(CIRGenModule &cgm, const Decl &d) {
assert(!cir::MissingFeatures::supportComdat());

Expand Down Expand Up @@ -749,6 +773,84 @@ CIRGenModule::getCIRLinkageVarDefinition(const VarDecl *vd, bool isConstant) {
return getCIRLinkageForDeclarator(vd, linkage, isConstant);
}

static cir::GlobalOp generateStringLiteral(mlir::Location loc,
mlir::TypedAttr c, CIRGenModule &cgm,
StringRef globalName) {
assert(!cir::MissingFeatures::addressSpace());

// Create a global variable for this string
// FIXME(cir): check for insertion point in module level.
cir::GlobalOp gv =
CIRGenModule::createGlobalOp(cgm, loc, globalName, c.getType());

// Set up extra information and add to the module
assert(!cir::MissingFeatures::opGlobalAlignment());
assert(!cir::MissingFeatures::opGlobalLinkage());
assert(!cir::MissingFeatures::opGlobalThreadLocal());
assert(!cir::MissingFeatures::opGlobalUnnamedAddr());
CIRGenModule::setInitializer(gv, c);
assert(!cir::MissingFeatures::supportComdat());
assert(!cir::MissingFeatures::opGlobalDSOLocal());
return gv;
}

// LLVM IR automatically uniques names when new llvm::GlobalVariables are
// created. This is handy, for example, when creating globals for string
// literals. Since we don't do that when creating cir::GlobalOp's, we need
// a mechanism to generate a unique name in advance.
//
// For now, this mechanism is only used in cases where we know that the
// name is compiler-generated, so we don't use the MLIR symbol table for
// the lookup.
std::string CIRGenModule::getUniqueGlobalName(const std::string &baseName) {
// If this is the first time we've generated a name for this basename, use
// it as is and start a counter for this base name.
auto it = cgGlobalNames.find(baseName);
if (it == cgGlobalNames.end()) {
cgGlobalNames[baseName] = 1;
return baseName;
}

std::string result =
baseName + "." + std::to_string(cgGlobalNames[baseName]++);
// There should not be any symbol with this name in the module.
assert(!mlir::SymbolTable::lookupSymbolIn(theModule, result));
return result;
}

/// Return a pointer to a constant array for the given string literal.
cir::GlobalOp CIRGenModule::getGlobalForStringLiteral(const StringLiteral *s,
StringRef name) {
mlir::Attribute c = getConstantArrayFromStringLiteral(s);

if (getLangOpts().WritableStrings) {
errorNYI(s->getSourceRange(),
"getGlobalForStringLiteral: Writable strings");
}

// Mangle the string literal if that's how the ABI merges duplicate strings.
// Don't do it if they are writable, since we don't want writes in one TU to
// affect strings in another.
if (getCXXABI().getMangleContext().shouldMangleStringLiteral(s) &&
!getLangOpts().WritableStrings) {
errorNYI(s->getSourceRange(),
"getGlobalForStringLiteral: mangle string literals");
}

// Unlike LLVM IR, CIR doesn't automatically unique names for globals, so
// we need to do that explicitly.
std::string uniqueName = getUniqueGlobalName(name.str());
mlir::Location loc = getLoc(s->getSourceRange());
auto typedC = llvm::cast<mlir::TypedAttr>(c);
assert(!cir::MissingFeatures::opGlobalAlignment());
cir::GlobalOp gv = generateStringLiteral(loc, typedC, *this, uniqueName);
assert(!cir::MissingFeatures::opGlobalDSOLocal());

assert(!cir::MissingFeatures::sanitizers());

return gv;
}

void CIRGenModule::emitDeclContext(const DeclContext *dc) {
for (Decl *decl : dc->decls()) {
// Unlike other DeclContexts, the contents of an ObjCImplDecl at TU scope
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ class CIRGenModule : public CIRGenTypeCache {
llvm::StringRef name, mlir::Type t,
mlir::Operation *insertPoint = nullptr);

llvm::StringMap<unsigned> cgGlobalNames;
std::string getUniqueGlobalName(const std::string &baseName);

/// Return the mlir::Value for the address of the given global variable.
/// If Ty is non-null and if the global doesn't exist, then it will be created
/// with the specified type instead of whatever the normal requested type
Expand All @@ -136,6 +139,14 @@ class CIRGenModule : public CIRGenTypeCache {
getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty = {},
ForDefinition_t isForDefinition = NotForDefinition);

/// Return a constant array for the given string.
mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);

/// Return a global symbol reference to a constant array for the given string
/// literal.
cir::GlobalOp getGlobalForStringLiteral(const StringLiteral *s,
llvm::StringRef name = ".str");

const TargetCIRGenInfo &getTargetCIRGenInfo();

/// Helpers to convert the presumed location of Clang's SourceLocation to an
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,18 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
result =
rewriter.create<mlir::LLVM::InsertValueOp>(loc, result, init, idx);
}
} else if (auto strAttr = mlir::dyn_cast<mlir::StringAttr>(attr.getElts())) {
// TODO(cir): this diverges from traditional lowering. Normally the string
// would be a global constant that is memcopied.
auto arrayTy = mlir::dyn_cast<cir::ArrayType>(strAttr.getType());
assert(arrayTy && "String attribute must have an array type");
mlir::Type eltTy = arrayTy.getElementType();
for (auto [idx, elt] : llvm::enumerate(strAttr)) {
auto init = rewriter.create<mlir::LLVM::ConstantOp>(
loc, converter->convertType(eltTy), elt);
result =
rewriter.create<mlir::LLVM::InsertValueOp>(loc, result, init, idx);
}
} else {
llvm_unreachable("unexpected ConstArrayAttr elements");
}
Expand Down
56 changes: 56 additions & 0 deletions clang/test/CIR/CodeGen/string-literals.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s

// LLVM: @[[STR1_GLOBAL:.*]] = dso_local global [2 x i8] c"1\00"
// LLVM: @[[STR2_GLOBAL:.*]] = dso_local global [1 x i8] zeroinitializer
// LLVM: @[[STR3_GLOBAL:.*]] = dso_local global [2 x i8] zeroinitializer

// OGCG: @[[STR1_GLOBAL:.*]] = private unnamed_addr constant [2 x i8] c"1\00"
// OGCG: @[[STR2_GLOBAL:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer
// OGCG: @[[STR3_GLOBAL:.*]] = private unnamed_addr constant [2 x i8] zeroinitializer

char *f1() {
return "1";
}

// CIR: cir.global external @[[STR1_GLOBAL:.*]] = #cir.const_array<"1\00" : !cir.array<!s8i x 2>> : !cir.array<!s8i x 2>
// CIR: cir.func @f1()
// CIR: %[[STR:.*]] = cir.get_global @[[STR1_GLOBAL]] : !cir.ptr<!cir.array<!s8i x 2>>

// LLVM: define ptr @f1()
// LLVM: store ptr @[[STR1_GLOBAL]], ptr {{.*}}

// OGCG: define {{.*}}ptr @f1()
// OGCG: ret ptr @[[STR1_GLOBAL]]

char *f2() {
return "";
}

// CIR: cir.global external @[[STR2_GLOBAL:.*]] = #cir.zero : !cir.array<!s8i x 1>
// CIR: cir.func @f2()
// CIR: %[[STR2:.*]] = cir.get_global @[[STR2_GLOBAL]] : !cir.ptr<!cir.array<!s8i x 1>>

// LLVM: define ptr @f2()
// LLVM: store ptr @[[STR2_GLOBAL]], ptr {{.*}}

// OGCG: define {{.*}}ptr @f2()
// OGCG: ret ptr @[[STR2_GLOBAL]]

char *f3() {
return "\00";
}

// CIR: cir.global external @[[STR3_GLOBAL:.*]] = #cir.zero : !cir.array<!s8i x 2>
// CIR: cir.func @f3()
// CIR: %[[STR3:.*]] = cir.get_global @[[STR3_GLOBAL]] : !cir.ptr<!cir.array<!s8i x 2>>

// LLVM: define ptr @f3()
// LLVM: store ptr @[[STR3_GLOBAL]], ptr {{.*}}

// OGCG: define {{.*}}ptr @f3()
// OGCG: ret ptr @[[STR3_GLOBAL]]