diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index fcc8ce7caf111..11461d3693d44 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -162,6 +162,17 @@ struct MissingFeatures { static bool addressIsKnownNonNull() { return false; } static bool addressPointerAuthInfo() { return false; } + // Atomic + static bool convertAtomicType() { return false; } + static bool atomicExpr() { return false; } + static bool atomicInitComplex() { return false; } + static bool atomicInitAggregate() { return false; } + static bool atomicCopyAggregateIntoAtomic() { return false; } + static bool atomicCopyComplexIntoAtomic() { return false; } + static bool atomicInfo() { return false; } + static bool atomicInfoGetAtomicPointer() { return false; } + static bool atomicInfoGetAtomicAddress() { return false; } + // Misc static bool abiArgInfo() { return false; } static bool addHeapAllocSiteMetadata() { return false; } @@ -197,6 +208,7 @@ struct MissingFeatures { static bool cudaSupport() { return false; } static bool cxxRecordStaticMembers() { return false; } static bool dataLayoutTypeAllocSize() { return false; } + static bool dataLayoutTypeStoreSize() { return false; } static bool deferredCXXGlobalInit() { return false; } static bool ehCleanupFlags() { return false; } static bool ehCleanupScope() { return false; } @@ -232,6 +244,7 @@ struct MissingFeatures { static bool objCBlocks() { return false; } static bool objCGC() { return false; } static bool objCLifetime() { return false; } + static bool openCL() { return false; } static bool openMP() { return false; } static bool opGlobalViewAttr() { return false; } static bool opTBAA() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp new file mode 100644 index 0000000000000..0cd9a22b1b7cc --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp @@ -0,0 +1,227 @@ +//===--- CIRGenAtomic.cpp - Emit CIR for atomic operations ----------------===// +// +// 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 file contains the code for emitting atomic operations. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenFunction.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace clang; +using namespace clang::CIRGen; +using namespace cir; + +namespace { +class AtomicInfo { + CIRGenFunction &cgf; + QualType atomicTy; + QualType valueTy; + uint64_t atomicSizeInBits = 0; + uint64_t valueSizeInBits = 0; + CharUnits atomicAlign; + CharUnits valueAlign; + TypeEvaluationKind evaluationKind = cir::TEK_Scalar; + LValue lvalue; + mlir::Location loc; + +public: + AtomicInfo(CIRGenFunction &cgf, LValue &lvalue, mlir::Location loc) + : cgf(cgf), loc(loc) { + assert(!lvalue.isGlobalReg()); + ASTContext &ctx = cgf.getContext(); + if (lvalue.isSimple()) { + atomicTy = lvalue.getType(); + if (auto *ty = atomicTy->getAs()) + valueTy = ty->getValueType(); + else + valueTy = atomicTy; + evaluationKind = cgf.getEvaluationKind(valueTy); + + TypeInfo valueTypeInfo = ctx.getTypeInfo(valueTy); + TypeInfo atomicTypeInfo = ctx.getTypeInfo(atomicTy); + uint64_t valueAlignInBits = valueTypeInfo.Align; + uint64_t atomicAlignInBits = atomicTypeInfo.Align; + valueSizeInBits = valueTypeInfo.Width; + atomicSizeInBits = atomicTypeInfo.Width; + assert(valueSizeInBits <= atomicSizeInBits); + assert(valueAlignInBits <= atomicAlignInBits); + + atomicAlign = ctx.toCharUnitsFromBits(atomicAlignInBits); + valueAlign = ctx.toCharUnitsFromBits(valueAlignInBits); + if (lvalue.getAlignment().isZero()) + lvalue.setAlignment(atomicAlign); + + this->lvalue = lvalue; + } else { + assert(!cir::MissingFeatures::atomicInfo()); + } + + assert(!cir::MissingFeatures::atomicInfo()); + } + + QualType getValueType() const { return valueTy; } + CharUnits getAtomicAlignment() const { return atomicAlign; } + TypeEvaluationKind getEvaluationKind() const { return evaluationKind; } + mlir::Value getAtomicPointer() const { + if (lvalue.isSimple()) + return lvalue.getPointer(); + assert(!cir::MissingFeatures::atomicInfoGetAtomicPointer()); + return nullptr; + } + Address getAtomicAddress() const { + mlir::Type elemTy; + if (lvalue.isSimple()) { + elemTy = lvalue.getAddress().getElementType(); + } else { + assert(!cir::MissingFeatures::atomicInfoGetAtomicAddress()); + } + return Address(getAtomicPointer(), elemTy, getAtomicAlignment()); + } + + /// Is the atomic size larger than the underlying value type? + /// + /// Note that the absence of padding does not mean that atomic + /// objects are completely interchangeable with non-atomic + /// objects: we might have promoted the alignment of a type + /// without making it bigger. + bool hasPadding() const { return (valueSizeInBits != atomicSizeInBits); } + + bool emitMemSetZeroIfNecessary() const; + + /// Copy an atomic r-value into atomic-layout memory. + void emitCopyIntoMemory(RValue rvalue) const; + + /// Project an l-value down to the value field. + LValue projectValue() const { + assert(lvalue.isSimple()); + Address addr = getAtomicAddress(); + if (hasPadding()) + llvm_unreachable("NYI"); + + assert(!cir::MissingFeatures::opTBAA()); + return LValue::makeAddr(addr, getValueType(), lvalue.getBaseInfo()); + } + +private: + bool requiresMemSetZero(mlir::Type ty) const; +}; +} // namespace + +/// Does a store of the given IR type modify the full expected width? +static bool isFullSizeType(CIRGenModule &cgm, mlir::Type ty, + uint64_t expectedSize) { + assert(!cir::MissingFeatures::dataLayoutTypeStoreSize()); + return true; +} + +/// Does the atomic type require memsetting to zero before initialization? +/// +/// The IR type is provided as a way of making certain queries faster. +bool AtomicInfo::requiresMemSetZero(mlir::Type ty) const { + // If the atomic type has size padding, we definitely need a memset. + if (hasPadding()) + return true; + + // Otherwise, do some simple heuristics to try to avoid it: + switch (getEvaluationKind()) { + // For scalars and complexes, check whether the store size of the + // type uses the full size. + case cir::TEK_Scalar: + return !isFullSizeType(cgf.cgm, ty, atomicSizeInBits); + case cir::TEK_Complex: + llvm_unreachable("NYI"); + + // Padding in structs has an undefined bit pattern. User beware. + case cir::TEK_Aggregate: + return false; + } + llvm_unreachable("bad evaluation kind"); +} + +bool AtomicInfo::emitMemSetZeroIfNecessary() const { + assert(lvalue.isSimple()); + Address addr = lvalue.getAddress(); + if (!requiresMemSetZero(addr.getElementType())) + return false; + + llvm_unreachable("NYI"); +} + +/// Copy an r-value into memory as part of storing to an atomic type. +/// This needs to create a bit-pattern suitable for atomic operations. +void AtomicInfo::emitCopyIntoMemory(RValue rvalue) const { + assert(lvalue.isSimple()); + + // If we have an r-value, the rvalue should be of the atomic type, + // which means that the caller is responsible for having zeroed + // any padding. Just do an aggregate copy of that type. + if (rvalue.isAggregate()) { + assert(!cir::MissingFeatures::atomicCopyAggregateIntoAtomic()); + cgf.cgm.errorNYI("copying aggregate into atomic lvalue is NYI"); + return; + } + + // Okay, otherwise we're copying stuff. + + // Zero out the buffer if necessary. + emitMemSetZeroIfNecessary(); + + // Drill past the padding if present. + LValue tempLValue = projectValue(); + + // Okay, store the rvalue in. + if (rvalue.isScalar()) { + cgf.emitStoreOfScalar(rvalue.getValue(), tempLValue, /*isInit=*/true); + } else { + assert(!cir::MissingFeatures::atomicCopyComplexIntoAtomic()); + cgf.cgm.errorNYI("copying complex into atomic lvalue is NYI"); + } +} + +RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) { + QualType atomicTy = e->getPtr()->getType()->getPointeeType(); + QualType memTy = atomicTy; + if (const auto *ty = atomicTy->getAs()) + memTy = ty->getValueType(); + + Address ptr = emitPointerWithAlignment(e->getPtr()); + + assert(!cir::MissingFeatures::openCL()); + if (e->getOp() == AtomicExpr::AO__c11_atomic_init) { + LValue lvalue = makeAddrLValue(ptr, atomicTy); + emitAtomicInit(e->getVal1(), lvalue); + return RValue::get(nullptr); + } + + assert(!cir::MissingFeatures::atomicExpr()); + cgm.errorNYI(e->getSourceRange(), "atomic expr is NYI"); + return RValue::get(nullptr); +} + +void CIRGenFunction::emitAtomicInit(Expr *init, LValue dest) { + AtomicInfo atomics(*this, dest, getLoc(init->getSourceRange())); + + switch (atomics.getEvaluationKind()) { + case cir::TEK_Scalar: { + mlir::Value value = emitScalarExpr(init); + atomics.emitCopyIntoMemory(RValue::get(value)); + return; + } + + case cir::TEK_Complex: + assert(!cir::MissingFeatures::atomicInitComplex()); + return; + + case cir::TEK_Aggregate: + assert(!cir::MissingFeatures::atomicInitAggregate()); + return; + } + + llvm_unreachable("bad evaluation kind"); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index c437b14dd8d1c..a53857c019b0d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -184,8 +184,11 @@ Address CIRGenFunction::emitPointerWithAlignment(const Expr *expr, if (const UnaryOperator *uo = dyn_cast(expr)) { // TODO(cir): maybe we should use cir.unary for pointers here instead. if (uo->getOpcode() == UO_AddrOf) { - cgm.errorNYI(expr->getSourceRange(), "emitPointerWithAlignment: unary &"); - return Address::invalid(); + LValue lv = emitLValue(uo->getSubExpr()); + if (baseInfo) + *baseInfo = lv.getBaseInfo(); + assert(!cir::MissingFeatures::opTBAA()); + return lv.getAddress(); } } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 3e06513bb6cf0..e0015f505e9ed 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -1060,6 +1060,10 @@ class ScalarExprEmitter : public StmtVisitor { return maybePromoteBoolResult(resOp.getResult(), resTy); } + + mlir::Value VisitAtomicExpr(AtomicExpr *e) { + return cgf.emitAtomicExpr(e).getValue(); + } }; LValue ScalarExprEmitter::emitCompoundAssignLValue( diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 065039ec041e0..37f5c1a35198e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -893,6 +893,9 @@ class CIRGenFunction : public CIRGenTypeCache { Address emitArrayToPointerDecay(const Expr *array); + RValue emitAtomicExpr(AtomicExpr *e); + void emitAtomicInit(Expr *init, LValue dest); + AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d, mlir::OpBuilder::InsertPoint ip = {}); @@ -1197,7 +1200,7 @@ class CIRGenFunction : public CIRGenTypeCache { /// reasonable to just ignore the returned alignment when it isn't from an /// explicit source. Address emitPointerWithAlignment(const clang::Expr *expr, - LValueBaseInfo *baseInfo); + LValueBaseInfo *baseInfo = nullptr); /// Emits a reference binding to the passed in expression. RValue emitReferenceBindingToExpr(const Expr *e); diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 2084b6d9e8989..baaf7cb50b96f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -491,6 +491,16 @@ mlir::Type CIRGenTypes::convertType(QualType type) { break; } + case Type::Atomic: { + QualType valueType = cast(ty)->getValueType(); + resultType = convertTypeForMem(valueType); + + // Pad out to the inflated size if necessary. + assert(!cir::MissingFeatures::convertAtomicType()); + + break; + } + default: cgm.errorNYI(SourceLocation(), "processing of type", type->getTypeClassName()); diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index 0832c4141a10f..661cecf8416b6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -190,6 +190,7 @@ class LValue { bool isSimple() const { return lvType == Simple; } bool isVectorElt() const { return lvType == VectorElt; } bool isBitField() const { return lvType == BitField; } + bool isGlobalReg() const { return lvType == GlobalReg; } bool isVolatile() const { return quals.hasVolatile(); } bool isVolatileQualified() const { return quals.hasVolatile(); } diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index ca3a329d0c56d..29aa5372c66f1 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -8,6 +8,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) add_clang_library(clangCIR CIRGenerator.cpp + CIRGenAtomic.cpp CIRGenBuilder.cpp CIRGenCall.cpp CIRGenClass.cpp diff --git a/clang/test/CIR/CodeGen/atomic.c b/clang/test/CIR/CodeGen/atomic.c new file mode 100644 index 0000000000000..8db4ae43d7389 --- /dev/null +++ b/clang/test/CIR/CodeGen/atomic.c @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +void f1(void) { + _Atomic(int) x = 42; +} + +// CIR-LABEL: @f1 +// CIR: %[[SLOT:.+]] = cir.alloca !s32i, !cir.ptr, ["x", init] {alignment = 4 : i64} +// CIR-NEXT: %[[INIT:.+]] = cir.const #cir.int<42> : !s32i +// CIR-NEXT: cir.store align(4) %[[INIT]], %[[SLOT]] : !s32i, !cir.ptr +// CIR: } + +// LLVM-LABEL: @f1 +// LLVM: %[[SLOT:.+]] = alloca i32, i64 1, align 4 +// LLVM-NEXT: store i32 42, ptr %[[SLOT]], align 4 +// LLVM: } + +// OGCG-LABEL: @f1 +// OGCG: %[[SLOT:.+]] = alloca i32, align 4 +// OGCG-NEXT: store i32 42, ptr %[[SLOT]], align 4 +// OGCG: } + +void f2(void) { + _Atomic(int) x; + __c11_atomic_init(&x, 42); +} + +// CIR-LABEL: @f2 +// CIR: %[[SLOT:.+]] = cir.alloca !s32i, !cir.ptr, ["x"] {alignment = 4 : i64} +// CIR-NEXT: %[[INIT:.+]] = cir.const #cir.int<42> : !s32i +// CIR-NEXT: cir.store align(4) %[[INIT]], %[[SLOT]] : !s32i, !cir.ptr +// CIR: } + +// LLVM-LABEL: @f2 +// LLVM: %[[SLOT:.+]] = alloca i32, i64 1, align 4 +// LLVM-NEXT: store i32 42, ptr %[[SLOT]], align 4 +// LLVM: } + +// OGCG-LABEL: @f2 +// OGCG: %[[SLOT:.+]] = alloca i32, align 4 +// OGCG-NEXT: store i32 42, ptr %[[SLOT]], align 4 +// OGCG: }