Skip to content

Commit 78921cd

Browse files
authored
[CIR] Upstream ArraySubscriptExpr for fixed size array (#134536)
This change adds ArraySubscriptExpr for fixed size ArrayType Issue #130197
1 parent 2667845 commit 78921cd

File tree

11 files changed

+556
-99
lines changed

11 files changed

+556
-99
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ struct MissingFeatures {
141141
static bool mangledNames() { return false; }
142142
static bool setDLLStorageClass() { return false; }
143143
static bool openMP() { return false; }
144+
static bool emitCheckedInBoundsGEP() { return false; }
145+
static bool preservedAccessIndexRegion() { return false; }
144146

145147
// Missing types
146148
static bool dataMemberType() { return false; }
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "CIRGenBuilder.h"
10+
11+
using namespace clang::CIRGen;
12+
13+
mlir::Value CIRGenBuilderTy::maybeBuildArrayDecay(mlir::Location loc,
14+
mlir::Value arrayPtr,
15+
mlir::Type eltTy) {
16+
const auto arrayPtrTy = mlir::cast<cir::PointerType>(arrayPtr.getType());
17+
const auto arrayTy = mlir::dyn_cast<cir::ArrayType>(arrayPtrTy.getPointee());
18+
19+
if (arrayTy) {
20+
const cir::PointerType flatPtrTy = getPointerTo(arrayTy.getEltType());
21+
return create<cir::CastOp>(loc, flatPtrTy, cir::CastKind::array_to_ptrdecay,
22+
arrayPtr);
23+
}
24+
25+
assert(arrayPtrTy.getPointee() == eltTy &&
26+
"flat pointee type must match original array element type");
27+
return arrayPtr;
28+
}
29+
30+
mlir::Value CIRGenBuilderTy::getArrayElement(mlir::Location arrayLocBegin,
31+
mlir::Location arrayLocEnd,
32+
mlir::Value arrayPtr,
33+
mlir::Type eltTy, mlir::Value idx,
34+
bool shouldDecay) {
35+
mlir::Value basePtr = arrayPtr;
36+
if (shouldDecay)
37+
basePtr = maybeBuildArrayDecay(arrayLocBegin, arrayPtr, eltTy);
38+
const mlir::Type flatPtrTy = basePtr.getType();
39+
return create<cir::PtrStrideOp>(arrayLocEnd, flatPtrTy, basePtr, idx);
40+
}

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,19 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
198198

199199
return create<cir::BinOp>(loc, cir::BinOpKind::Div, lhs, rhs);
200200
}
201+
202+
/// Create a cir.ptr_stride operation to get access to an array element.
203+
/// \p idx is the index of the element to access, \p shouldDecay is true if
204+
/// the result should decay to a pointer to the element type.
205+
mlir::Value getArrayElement(mlir::Location arrayLocBegin,
206+
mlir::Location arrayLocEnd, mlir::Value arrayPtr,
207+
mlir::Type eltTy, mlir::Value idx,
208+
bool shouldDecay);
209+
210+
/// Returns a decayed pointer to the first element of the array
211+
/// pointed to by \p arrayPtr.
212+
mlir::Value maybeBuildArrayDecay(mlir::Location loc, mlir::Value arrayPtr,
213+
mlir::Type eltTy);
201214
};
202215

203216
} // namespace clang::CIRGen

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "Address.h"
1414
#include "CIRGenFunction.h"
15+
#include "CIRGenModule.h"
1516
#include "CIRGenValue.h"
1617
#include "mlir/IR/BuiltinAttributes.h"
1718
#include "clang/AST/Attr.h"
@@ -430,6 +431,143 @@ LValue CIRGenFunction::emitUnaryOpLValue(const UnaryOperator *e) {
430431
llvm_unreachable("Unknown unary operator kind!");
431432
}
432433

434+
/// If the specified expr is a simple decay from an array to pointer,
435+
/// return the array subexpression.
436+
/// FIXME: this could be abstracted into a common AST helper.
437+
static const Expr *getSimpleArrayDecayOperand(const Expr *e) {
438+
// If this isn't just an array->pointer decay, bail out.
439+
const auto *castExpr = dyn_cast<CastExpr>(e);
440+
if (!castExpr || castExpr->getCastKind() != CK_ArrayToPointerDecay)
441+
return nullptr;
442+
443+
// If this is a decay from variable width array, bail out.
444+
const Expr *subExpr = castExpr->getSubExpr();
445+
if (subExpr->getType()->isVariableArrayType())
446+
return nullptr;
447+
448+
return subExpr;
449+
}
450+
451+
static cir::IntAttr getConstantIndexOrNull(mlir::Value idx) {
452+
// TODO(cir): should we consider using MLIRs IndexType instead of IntegerAttr?
453+
if (auto constantOp = dyn_cast<cir::ConstantOp>(idx.getDefiningOp()))
454+
return mlir::dyn_cast<cir::IntAttr>(constantOp.getValue());
455+
return {};
456+
}
457+
458+
static CharUnits getArrayElementAlign(CharUnits arrayAlign, mlir::Value idx,
459+
CharUnits eltSize) {
460+
// If we have a constant index, we can use the exact offset of the
461+
// element we're accessing.
462+
const cir::IntAttr constantIdx = getConstantIndexOrNull(idx);
463+
if (constantIdx) {
464+
const CharUnits offset = constantIdx.getValue().getZExtValue() * eltSize;
465+
return arrayAlign.alignmentAtOffset(offset);
466+
}
467+
// Otherwise, use the worst-case alignment for any element.
468+
return arrayAlign.alignmentOfArrayElement(eltSize);
469+
}
470+
471+
static QualType getFixedSizeElementType(const ASTContext &astContext,
472+
const VariableArrayType *vla) {
473+
QualType eltType;
474+
do {
475+
eltType = vla->getElementType();
476+
} while ((vla = astContext.getAsVariableArrayType(eltType)));
477+
return eltType;
478+
}
479+
480+
static mlir::Value emitArraySubscriptPtr(CIRGenFunction &cgf,
481+
mlir::Location beginLoc,
482+
mlir::Location endLoc, mlir::Value ptr,
483+
mlir::Type eltTy, mlir::Value idx,
484+
bool shouldDecay) {
485+
CIRGenModule &cgm = cgf.getCIRGenModule();
486+
// TODO(cir): LLVM codegen emits in bound gep check here, is there anything
487+
// that would enhance tracking this later in CIR?
488+
assert(!cir::MissingFeatures::emitCheckedInBoundsGEP());
489+
return cgm.getBuilder().getArrayElement(beginLoc, endLoc, ptr, eltTy, idx,
490+
shouldDecay);
491+
}
492+
493+
static Address emitArraySubscriptPtr(CIRGenFunction &cgf,
494+
mlir::Location beginLoc,
495+
mlir::Location endLoc, Address addr,
496+
QualType eltType, mlir::Value idx,
497+
mlir::Location loc, bool shouldDecay) {
498+
499+
// Determine the element size of the statically-sized base. This is
500+
// the thing that the indices are expressed in terms of.
501+
if (const VariableArrayType *vla =
502+
cgf.getContext().getAsVariableArrayType(eltType)) {
503+
eltType = getFixedSizeElementType(cgf.getContext(), vla);
504+
}
505+
506+
// We can use that to compute the best alignment of the element.
507+
const CharUnits eltSize = cgf.getContext().getTypeSizeInChars(eltType);
508+
const CharUnits eltAlign =
509+
getArrayElementAlign(addr.getAlignment(), idx, eltSize);
510+
511+
assert(!cir::MissingFeatures::preservedAccessIndexRegion());
512+
const mlir::Value eltPtr =
513+
emitArraySubscriptPtr(cgf, beginLoc, endLoc, addr.getPointer(),
514+
addr.getElementType(), idx, shouldDecay);
515+
const mlir::Type elementType = cgf.convertTypeForMem(eltType);
516+
return Address(eltPtr, elementType, eltAlign);
517+
}
518+
519+
LValue
520+
CIRGenFunction::emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e) {
521+
if (e->getBase()->getType()->isVectorType() &&
522+
!isa<ExtVectorElementExpr>(e->getBase())) {
523+
cgm.errorNYI(e->getSourceRange(), "emitArraySubscriptExpr: VectorType");
524+
return LValue::makeAddr(Address::invalid(), e->getType(), LValueBaseInfo());
525+
}
526+
527+
if (isa<ExtVectorElementExpr>(e->getBase())) {
528+
cgm.errorNYI(e->getSourceRange(),
529+
"emitArraySubscriptExpr: ExtVectorElementExpr");
530+
return LValue::makeAddr(Address::invalid(), e->getType(), LValueBaseInfo());
531+
}
532+
533+
if (getContext().getAsVariableArrayType(e->getType())) {
534+
cgm.errorNYI(e->getSourceRange(),
535+
"emitArraySubscriptExpr: VariableArrayType");
536+
return LValue::makeAddr(Address::invalid(), e->getType(), LValueBaseInfo());
537+
}
538+
539+
if (e->getType()->getAs<ObjCObjectType>()) {
540+
cgm.errorNYI(e->getSourceRange(), "emitArraySubscriptExpr: ObjCObjectType");
541+
return LValue::makeAddr(Address::invalid(), e->getType(), LValueBaseInfo());
542+
}
543+
544+
// The index must always be an integer, which is not an aggregate. Emit it
545+
// in lexical order (this complexity is, sadly, required by C++17).
546+
assert((e->getIdx() == e->getLHS() || e->getIdx() == e->getRHS()) &&
547+
"index was neither LHS nor RHS");
548+
const mlir::Value idx = emitScalarExpr(e->getIdx());
549+
if (const Expr *array = getSimpleArrayDecayOperand(e->getBase())) {
550+
LValue arrayLV;
551+
if (const auto *ase = dyn_cast<ArraySubscriptExpr>(array))
552+
arrayLV = emitArraySubscriptExpr(ase);
553+
else
554+
arrayLV = emitLValue(array);
555+
556+
// Propagate the alignment from the array itself to the result.
557+
const Address addr = emitArraySubscriptPtr(
558+
*this, cgm.getLoc(array->getBeginLoc()), cgm.getLoc(array->getEndLoc()),
559+
arrayLV.getAddress(), e->getType(), idx, cgm.getLoc(e->getExprLoc()),
560+
/*shouldDecay=*/true);
561+
562+
return LValue::makeAddr(addr, e->getType(), LValueBaseInfo());
563+
}
564+
565+
// The base must be a pointer; emit it with an estimate of its alignment.
566+
cgm.errorNYI(e->getSourceRange(),
567+
"emitArraySubscriptExpr: The base must be a pointer");
568+
return {};
569+
}
570+
433571
LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) {
434572
// Comma expressions just emit their LHS then their RHS as an l-value.
435573
if (e->getOpcode() == BO_Comma) {

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,16 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
158158
mlir::Value VisitCastExpr(CastExpr *e);
159159
mlir::Value VisitCallExpr(const CallExpr *e);
160160

161+
mlir::Value VisitArraySubscriptExpr(ArraySubscriptExpr *e) {
162+
if (e->getBase()->getType()->isVectorType()) {
163+
assert(!cir::MissingFeatures::scalableVectors());
164+
cgf.getCIRGenModule().errorNYI("VisitArraySubscriptExpr: VectorType");
165+
return {};
166+
}
167+
// Just load the lvalue formed by the subscript expression.
168+
return emitLoadOfLValue(e);
169+
}
170+
161171
mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) {
162172
return VisitCastExpr(e);
163173
}

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
509509
std::string("l-value not implemented for '") +
510510
e->getStmtClassName() + "'");
511511
return LValue();
512+
case Expr::ArraySubscriptExprClass:
513+
return emitArraySubscriptExpr(cast<ArraySubscriptExpr>(e));
512514
case Expr::UnaryOperatorClass:
513515
return emitUnaryOpLValue(cast<UnaryOperator>(e));
514516
case Expr::BinaryOperatorClass:

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,8 @@ class CIRGenFunction : public CIRGenTypeCache {
434434
/// should be returned.
435435
RValue emitAnyExpr(const clang::Expr *e);
436436

437+
LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);
438+
437439
AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d);
438440

439441
/// Emit code and set up symbol table for a variable declaration with auto,

clang/lib/CIR/CodeGen/CIRGenValue.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
#include "mlir/IR/Value.h"
2525

26+
#include "clang/CIR/MissingFeatures.h"
27+
2628
namespace clang::CIRGen {
2729

2830
/// This trivial value class is used to represent the result of an

clang/lib/CIR/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
88

99
add_clang_library(clangCIR
1010
CIRGenerator.cpp
11+
CIRGenBuilder.cpp
1112
CIRGenCall.cpp
1213
CIRGenDecl.cpp
1314
CIRGenDeclOpenACC.cpp

0 commit comments

Comments
 (0)