Index: polly/trunk/include/polly/ScopInfo.h =================================================================== --- polly/trunk/include/polly/ScopInfo.h +++ polly/trunk/include/polly/ScopInfo.h @@ -59,7 +59,70 @@ class SCEVAffFunc; class Comparison; -//===----------------------------------------------------------------------===// +/// @brief A class to store information about arrays in the SCoP. +/// +/// Objects are accessible via the ScoP, MemoryAccess or the id associated with +/// the MemoryAccess access function. +/// +class ScopArrayInfo { +public: + /// @brief Construct a ScopArrayInfo object. + /// + /// @param BasePtr The array base pointer. + /// @param AccessType The type used to access this array. + /// @param IslCtx The isl context used to create the base pointer id. + /// @param DimensionSizes A vector containing the size of each dimension. + ScopArrayInfo(Value *BasePtr, Type *AccessType, isl_ctx *IslCtx, + const SmallVector &DimensionSizes); + + /// @brief Destructor to free the isl id of the base pointer. + ~ScopArrayInfo(); + + /// @brief Return the base pointer. + Value *getBasePtr() const { return BasePtr; } + + /// @brief Return the number of dimensions. + unsigned getNumberOfDimensions() const { return DimensionSizes.size(); } + + /// @brief Return the size of dimension @p dim. + const SCEV *getDimensionSize(unsigned dim) const { + assert(dim < getNumberOfDimensions() && "Invalid dimension"); + return DimensionSizes[dim]; + } + + /// @brief Return the type used to access this array in the SCoP. + Type *getType() const { return AccessType; } + + /// @brief Return the isl id for the base pointer. + __isl_give isl_id *getBasePtrId() const; + + /// @brief Dump a readable representation to stderr. + void dump() const; + + /// @brief Print a readable representation to @p OS. + void print(raw_ostream &OS) const; + + /// @brief Access the ScopArrayInfo associated with an access function. + static const ScopArrayInfo * + getFromAccessFunction(__isl_keep isl_pw_multi_aff *PMA); + + /// @brief Access the ScopArrayInfo associated with an isl Id. + static const ScopArrayInfo *getFromId(__isl_take isl_id *Id); + +private: + /// @brief The base pointer. + Value *BasePtr; + + /// @brief The type used to access this array. + Type *AccessType; + + /// @brief The isl id for the base pointer. + isl_id *Id; + + /// @brief The sizes of each dimension. + SmallVector DimensionSizes; +}; + /// @brief Represent memory accesses in statements. class MemoryAccess { public: @@ -148,11 +211,12 @@ public: /// @brief Create a memory access from an access in LLVM-IR. /// - /// @param Access The memory access. - /// @param Statement The statement that contains the access. - /// @param SE The ScalarEvolution analysis. + /// @param Access The memory access. + /// @param AccInst The access instruction. + /// @param Statement The statement that contains the access. + /// @param SAI The ScopArrayInfo object for this base pointer. MemoryAccess(const IRAccess &Access, Instruction *AccInst, - ScopStmt *Statement); + ScopStmt *Statement, const ScopArrayInfo *SAI); ~MemoryAccess(); @@ -188,6 +252,9 @@ /// @brief Get the base array isl_id for this access. __isl_give isl_id *getArrayId() const; + /// @brief Get the ScopArrayInfo object for the base address. + const ScopArrayInfo *getScopArrayInfo() const; + /// @brief Return a string representation of the accesse's reduction type. const std::string getReductionOperatorStr() const; @@ -534,6 +601,9 @@ /// Constraints on parameters. isl_set *Context; + /// @brief A map to remember ScopArrayInfo objects for all base pointers. + DenseMap ScopArrayInfoMap; + /// @brief The assumptions under which this scop was built. /// /// When constructing a scop sometimes the exact representation of a statement @@ -728,6 +798,13 @@ const_reverse_iterator rend() const { return Stmts.rend(); } //@} + /// @brief Return the (possibly new) ScopArrayInfo object for @p Access. + const ScopArrayInfo *getOrCreateScopArrayInfo(const IRAccess &Access, + Instruction *AccessInst); + + /// @brief Return the cached ScopArrayInfo object for @p BasePtr. + const ScopArrayInfo *getScopArrayInfo(Value *BasePtr); + void setContext(isl_set *NewContext); /// @brief Align the parameters in the statement to the scop context Index: polly/trunk/lib/Analysis/ScopInfo.cpp =================================================================== --- polly/trunk/lib/Analysis/ScopInfo.cpp +++ polly/trunk/lib/Analysis/ScopInfo.cpp @@ -278,6 +278,43 @@ return L->getLoopDepth() - outerLoop->getLoopDepth(); } +ScopArrayInfo::ScopArrayInfo(Value *BasePtr, Type *AccessType, isl_ctx *Ctx, + const SmallVector &DimensionSizes) + : BasePtr(BasePtr), AccessType(AccessType), DimensionSizes(DimensionSizes) { + const std::string BasePtrName = getIslCompatibleName("MemRef_", BasePtr, ""); + Id = isl_id_alloc(Ctx, BasePtrName.c_str(), this); +} + +ScopArrayInfo::~ScopArrayInfo() { isl_id_free(Id); } + +isl_id *ScopArrayInfo::getBasePtrId() const { return isl_id_copy(Id); } + +void ScopArrayInfo::dump() const { print(errs()); } + +void ScopArrayInfo::print(raw_ostream &OS) const { + OS << "ScopArrayInfo:\n"; + OS << " Base: " << *getBasePtr() << "\n"; + OS << " Type: " << *getType() << "\n"; + OS << " Dimension Sizes:\n"; + for (unsigned u = 0; u < getNumberOfDimensions(); u++) + OS << " " << u << ") " << *DimensionSizes[u] << "\n"; + OS << "\n"; +} + +const ScopArrayInfo * +ScopArrayInfo::getFromAccessFunction(__isl_keep isl_pw_multi_aff *PMA) { + isl_id *Id = isl_pw_multi_aff_get_tuple_id(PMA, isl_dim_out); + assert(Id && "Output dimension didn't have an ID"); + return getFromId(Id); +} + +const ScopArrayInfo *ScopArrayInfo::getFromId(isl_id *Id) { + void *User = isl_id_get_user(Id); + const ScopArrayInfo *SAI = static_cast(User); + isl_id_free(Id); + return SAI; +} + const std::string MemoryAccess::getReductionOperatorStr(MemoryAccess::ReductionType RT) { switch (RT) { @@ -348,6 +385,14 @@ llvm_unreachable("Unknown IRAccess type!"); } +const ScopArrayInfo *MemoryAccess::getScopArrayInfo() const { + isl_id *ArrayId = getArrayId(); + void *User = isl_id_get_user(ArrayId); + const ScopArrayInfo *SAI = static_cast(User); + isl_id_free(ArrayId); + return SAI; +} + isl_id *MemoryAccess::getArrayId() const { return isl_map_get_tuple_id(AccessRelation, isl_dim_out); } @@ -433,14 +478,15 @@ } MemoryAccess::MemoryAccess(const IRAccess &Access, Instruction *AccInst, - ScopStmt *Statement) + ScopStmt *Statement, const ScopArrayInfo *SAI) : Type(getMemoryAccessType(Access)), Statement(Statement), Inst(AccInst), newAccessRelation(nullptr) { isl_ctx *Ctx = Statement->getIslCtx(); BaseAddr = Access.getBase(); BaseName = getIslCompatibleName("MemRef_", getBaseAddr(), ""); - isl_id *BaseAddrId = isl_id_alloc(Ctx, getBaseName().c_str(), nullptr); + + isl_id *BaseAddrId = SAI->getBasePtrId(); if (!Access.isAffine()) { // We overapproximate non-affine accesses with a possible access to the @@ -666,18 +712,23 @@ } void ScopStmt::buildAccesses(TempScop &tempScop, const Region &CurRegion) { - for (auto &&Access : *tempScop.getAccessFunctions(BB)) { - MemAccs.push_back(new MemoryAccess(Access.first, Access.second, this)); + for (const auto &AccessPair : *tempScop.getAccessFunctions(BB)) { + const IRAccess &Access = AccessPair.first; + Instruction *AccessInst = AccessPair.second; + + const ScopArrayInfo *SAI = + getParent()->getOrCreateScopArrayInfo(Access, AccessInst); + MemAccs.push_back(new MemoryAccess(Access, AccessInst, this, SAI)); // We do not track locations for scalar memory accesses at the moment. // // We do not have a use for this information at the moment. If we need this // at some point, the "instruction -> access" mapping needs to be enhanced // as a single instruction could then possibly perform multiple accesses. - if (!Access.first.isScalar()) { - assert(!InstructionToAccess.count(Access.second) && + if (!Access.isScalar()) { + assert(!InstructionToAccess.count(AccessInst) && "Unexpected 1-to-N mapping on instruction to access map!"); - InstructionToAccess[Access.second] = MemAccs.back(); + InstructionToAccess[AccessInst] = MemAccs.back(); } } } @@ -1373,6 +1424,10 @@ for (ScopStmt *Stmt : *this) delete Stmt; + // Free the ScopArrayInfo objects. + for (auto &ScopArrayInfoPair : ScopArrayInfoMap) + delete ScopArrayInfoPair.second; + // Free the alias groups for (MinMaxVectorTy *MinMaxAccesses : MinMaxAliasGroups) { for (MinMaxAccessTy &MMA : *MinMaxAccesses) { @@ -1383,6 +1438,26 @@ } } +const ScopArrayInfo *Scop::getOrCreateScopArrayInfo(const IRAccess &Access, + Instruction *AccessInst) { + Value *BasePtr = Access.getBase(); + const ScopArrayInfo *&SAI = ScopArrayInfoMap[BasePtr]; + if (!SAI) { + Type *AccessType = getPointerOperand(*AccessInst)->getType(); + SAI = new ScopArrayInfo(BasePtr, AccessType, getIslCtx(), Access.Sizes); + } + return SAI; +} + +const ScopArrayInfo *Scop::getScopArrayInfo(Value *BasePtr) { + const SCEV *PtrSCEV = SE->getSCEV(BasePtr); + const SCEVUnknown *PtrBaseSCEV = + cast(SE->getPointerBase(PtrSCEV)); + const ScopArrayInfo *SAI = ScopArrayInfoMap[PtrBaseSCEV->getValue()]; + assert(SAI && "No ScopArrayInfo available for this base pointer"); + return SAI; +} + std::string Scop::getContextStr() const { return stringFromIslObj(Context); } std::string Scop::getAssumedContextStr() const { return stringFromIslObj(AssumedContext); Index: polly/trunk/lib/CodeGen/IslCodeGeneration.cpp =================================================================== --- polly/trunk/lib/CodeGen/IslCodeGeneration.cpp +++ polly/trunk/lib/CodeGen/IslCodeGeneration.cpp @@ -23,7 +23,6 @@ #include "polly/CodeGen/BlockGenerators.h" #include "polly/CodeGen/CodeGeneration.h" #include "polly/CodeGen/IslAst.h" -#include "polly/CodeGen/IslExprBuilder.h" #include "polly/CodeGen/LoopGenerators.h" #include "polly/CodeGen/Utils.h" #include "polly/Dependences.h" Index: polly/trunk/lib/CodeGen/IslExprBuilder.cpp =================================================================== --- polly/trunk/lib/CodeGen/IslExprBuilder.cpp +++ polly/trunk/lib/CodeGen/IslExprBuilder.cpp @@ -11,6 +11,7 @@ #include "polly/CodeGen/IslExprBuilder.h" +#include "polly/ScopInfo.h" #include "polly/Support/GICHelper.h" #include "llvm/Support/Debug.h" @@ -103,39 +104,28 @@ assert(isl_ast_expr_get_op_n_arg(Expr) == 2 && "Multidimensional access functions are not supported yet"); - Value *Base, *IndexOp, *Zero, *Access; - SmallVector Indices; - Type *PtrElTy; + Value *Base, *IndexOp, *Access; + isl_ast_expr *BaseExpr; + isl_id *BaseId; + + BaseExpr = isl_ast_expr_get_op_arg(Expr, 0); + BaseId = isl_ast_expr_get_id(BaseExpr); + isl_ast_expr_free(BaseExpr); - Base = create(isl_ast_expr_get_op_arg(Expr, 0)); + const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(BaseId); + Base = SAI->getBasePtr(); assert(Base->getType()->isPointerTy() && "Access base should be a pointer"); + const Twine &BaseName = Base->getName(); + + if (Base->getType() != SAI->getType()) + Base = Builder.CreateBitCast(Base, SAI->getType(), + "polly.access.cast." + BaseName); IndexOp = create(isl_ast_expr_get_op_arg(Expr, 1)); assert(IndexOp->getType()->isIntegerTy() && "Access index should be an integer"); - Zero = ConstantInt::getNullValue(IndexOp->getType()); - - // If base is a array type like, - // int A[N][M][K]; - // we have to adjust the GEP. The easiest way is to transform accesses like, - // A[i][j][k] - // into equivalent ones like, - // A[0][0][ i*N*M + j*M + k] - // because SCEV already folded the "peudo dimensions" into one. Thus our index - // operand will be 'i*N*M + j*M + k' anyway. - PtrElTy = Base->getType()->getPointerElementType(); - while (PtrElTy->isArrayTy()) { - Indices.push_back(Zero); - PtrElTy = PtrElTy->getArrayElementType(); - } - - Indices.push_back(IndexOp); - assert((PtrElTy->isIntOrIntVectorTy() || PtrElTy->isFPOrFPVectorTy() || - PtrElTy->isPtrOrPtrVectorTy()) && - "We do not yet change the type of the access base during code " - "generation."); - Access = Builder.CreateGEP(Base, Indices, "polly.access." + Base->getName()); + Access = Builder.CreateGEP(Base, IndexOp, "polly.access." + BaseName); isl_ast_expr_free(Expr); return Access; Index: polly/trunk/lib/Exchange/JSONExporter.cpp =================================================================== --- polly/trunk/lib/Exchange/JSONExporter.cpp +++ polly/trunk/lib/Exchange/JSONExporter.cpp @@ -277,6 +277,9 @@ return false; } + isl_id *OutId = isl_map_get_tuple_id(currentAccessMap, isl_dim_out); + newAccessMap = isl_map_set_tuple_id(newAccessMap, isl_dim_out, OutId); + // We keep the old alignment, thus we cannot allow accesses to memory // locations that were not accessed before. isl_set *newAccessSet = isl_map_range(isl_map_copy(newAccessMap)); Index: polly/trunk/test/Isl/CodeGen/MemAccess/codegen_constant_offset.ll =================================================================== --- polly/trunk/test/Isl/CodeGen/MemAccess/codegen_constant_offset.ll +++ polly/trunk/test/Isl/CodeGen/MemAccess/codegen_constant_offset.ll @@ -40,4 +40,4 @@ for.end: ; preds = %for.cond ret i32 0 } -; CHECK: load i32* getelementptr inbounds ([100 x i32]* @A, i64 0, i64 10) +; CHECK: load i32* getelementptr inbounds ([100 x i32]* @A, i{{(32|64)}} 0, i{{(32|64)}} 10) Index: polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple.ll =================================================================== --- polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple.ll +++ polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple.ll @@ -40,4 +40,4 @@ for.end: ; preds = %for.cond ret i32 0 } -; CHECK: load i32* getelementptr inbounds ([100 x i32]* @A, i64 0, i64 0) +; CHECK: load i32* getelementptr inbounds ([100 x i32]* @A, i{{(32|64)}} 0, i{{(32|64)}} 0) Index: polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple_float.ll =================================================================== --- polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple_float.ll +++ polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple_float.ll @@ -38,4 +38,4 @@ for.end: ; preds = %for.cond ret i32 0 } -; CHECK: load float* getelementptr inbounds ([100 x float]* @A, i64 0, i64 0) +; CHECK: load float* getelementptr inbounds ([100 x float]* @A, i{{(32|64)}} 0, i{{(32|64)}} 0) Index: polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple_md.ll =================================================================== --- polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple_md.ll +++ polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple_md.ll @@ -63,7 +63,7 @@ ; WITHCONST: %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[IVIn]] ; WITHCONST: %[[SUM1:[._a-zA-Z0-9]+]] = add nsw i64 %[[MUL1]], %[[MUL2]] ; WITHCONST: %[[SUM2:[._a-zA-Z0-9]+]] = add nsw i64 %[[SUM1]], 5 -; WITHCONST: %[[ACC:[._a-zA-Z0-9]*]] = getelementptr [1040 x i32]* @A, i64 0, i64 %[[SUM2]] +; WITHCONST: %[[ACC:[._a-zA-Z0-9]*]] = getelementptr i32* getelementptr inbounds ([1040 x i32]* @A, i{{(32|64)}} 0, i{{(32|64)}} 0), i64 %[[SUM2]] ; WITHCONST: store i32 100, i32* %[[ACC]] ; WITHOUTCONST: %[[IVOut:polly.indvar[0-9]*]] = phi i64 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ] @@ -71,5 +71,5 @@ ; WITHOUTCONST: %[[MUL1:[._a-zA-Z0-9]+]] = mul nsw i64 16, %[[IVOut]] ; WITHOUTCONST: %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[IVIn]] ; WITHOUTCONST: %[[SUM1:[._a-zA-Z0-9]+]] = add nsw i64 %[[MUL1]], %[[MUL2]] -; WITHOUTCONST: %[[ACC:[._a-zA-Z0-9]*]] = getelementptr [1040 x i32]* @A, i64 0, i64 %[[SUM1]] +; WITHOUTCONST: %[[ACC:[._a-zA-Z0-9]*]] = getelementptr i32* getelementptr inbounds ([1040 x i32]* @A, i{{(32|64)}} 0, i{{(32|64)}} 0), i64 %[[SUM1]] ; WITHOUTCONST: store i32 100, i32* %[[ACC]] Index: polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple_md_float.ll =================================================================== --- polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple_md_float.ll +++ polly/trunk/test/Isl/CodeGen/MemAccess/codegen_simple_md_float.ll @@ -59,7 +59,7 @@ ; WITHCONST: %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[IVIn]] ; WITHCONST: %[[SUM1:[._a-zA-Z0-9]+]] = add nsw i64 %[[MUL1]], %[[MUL2]] ; WITHCONST: %[[SUM2:[._a-zA-Z0-9]+]] = add nsw i64 %[[SUM1]], 5 -; WITHCONST: %[[ACC:[._a-zA-Z0-9]*]] = getelementptr [1040 x float]* @A, i64 0, i64 %[[SUM2]] +; WITHCONST: %[[ACC:[._a-zA-Z0-9]*]] = getelementptr float* getelementptr inbounds ([1040 x float]* @A, i{{(32|64)}} 0, i{{(32|64)}} 0), i64 %[[SUM2]] ; WITHCONST: store float 1.000000e+02, float* %[[ACC]] ; WITHOUTCONST: %[[IVOut:polly.indvar[0-9]*]] = phi i64 [ 0, %polly.loop_preheader{{[0-9]*}} ], [ %polly.indvar_next{{[0-9]*}}, %polly.{{[._a-zA-Z0-9]*}} ] @@ -67,5 +67,5 @@ ; WITHOUTCONST: %[[MUL1:[._a-zA-Z0-9]+]] = mul nsw i64 16, %[[IVOut]] ; WITHOUTCONST: %[[MUL2:[._a-zA-Z0-9]+]] = mul nsw i64 2, %[[IVIn]] ; WITHOUTCONST: %[[SUM1:[._a-zA-Z0-9]+]] = add nsw i64 %[[MUL1]], %[[MUL2]] -; WITHOUTCONST: %[[ACC:[._a-zA-Z0-9]*]] = getelementptr [1040 x float]* @A, i64 0, i64 %[[SUM1]] +; WITHOUTCONST: %[[ACC:[._a-zA-Z0-9]*]] = getelementptr float* getelementptr inbounds ([1040 x float]* @A, i{{(32|64)}} 0, i{{(32|64)}} 0), i64 %[[SUM1]] ; WITHOUTCONST: store float 1.000000e+02, float* %[[ACC]] Index: polly/trunk/test/Isl/CodeGen/aliasing_different_base_and_access_type.ll =================================================================== --- polly/trunk/test/Isl/CodeGen/aliasing_different_base_and_access_type.ll +++ polly/trunk/test/Isl/CodeGen/aliasing_different_base_and_access_type.ll @@ -0,0 +1,44 @@ +; RUN: opt %loadPolly -S -polly-code-generator=isl -polly-codegen-isl < %s | FileCheck %s +; +; We have to cast %B to "short *" before we create RTCs. +; +; CHECK: entry: +; CHECK-NEXT: %polly.access.cast.B = bitcast i32* %B to i16* +; CHECK-NEXT: %polly.access.B = getelementptr i16* %polly.access.cast.B, i64 1024 +; +; We should never access %B as an i32 pointer: +; +; CHECK-NOT: getelementptr i32* %B +; +; void jd(int *A, int *B) { +; for (int i = 0; i < 1024; i++) +; A[i] = ((short *)B)[i]; +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @jd(i32* %A, i32* %B) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1024 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %tmp = bitcast i32* %B to i16* + %arrayidx = getelementptr inbounds i16* %tmp, i64 %indvars.iv + %tmp1 = load i16* %arrayidx, align 2 + %conv = sext i16 %tmp1 to i32 + %arrayidx2 = getelementptr inbounds i32* %A, i64 %indvars.iv + store i32 %conv, i32* %arrayidx2, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} Index: polly/trunk/test/Isl/CodeGen/aliasing_struct_element.ll =================================================================== --- polly/trunk/test/Isl/CodeGen/aliasing_struct_element.ll +++ polly/trunk/test/Isl/CodeGen/aliasing_struct_element.ll @@ -0,0 +1,53 @@ +; RUN: opt %loadPolly -S -polly-code-generator=isl -polly-codegen-isl < %s | FileCheck %s +; +; We should only access (or compute the address of) "the first element" of %S +; as it is a single struct not a struct array. The maximal access to S, thus +; S->B[1023] is for ScalarEvolution an access with offset of 1423, 1023 for the +; index inside the B part of S and 400 to skip the Dummy array in S. Note that +; these numbers are relative to the actual type of &S->B[i] (char*) not to the +; type of S (struct st *) or something else. +; +; Verify that we do not use the offset 1423 into a non existent S array when we +; compute runtime alias checks but treat it as if it was a char array. +; +; CHECK: %polly.access.cast.S = bitcast %struct.st* %S to i8* +; CHECK: %polly.access.S = getelementptr i8* %polly.access.cast.S, i64 1424 +; +; struct st { +; int Dummy[100]; +; char B[100]; +; }; +; +; void jd(int *A, struct st *S) { +; for (int i = 0; i < 1024; i++) +; A[i] = S->B[i]; +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +%struct.st = type { [100 x i32], [100 x i8] } + +define void @jd(i32* %A, %struct.st* %S) { +entry: + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %entry ] + %exitcond = icmp ne i64 %indvars.iv, 1024 + br i1 %exitcond, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %arrayidx = getelementptr inbounds %struct.st* %S, i64 0, i32 1, i64 %indvars.iv + %tmp = load i8* %arrayidx, align 1 + %conv = sext i8 %tmp to i32 + %arrayidx2 = getelementptr inbounds i32* %A, i64 %indvars.iv + store i32 %conv, i32* %arrayidx2, align 4 + br label %for.inc + +for.inc: ; preds = %for.body + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +}