26#include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
27#include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
28#include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h"
29#include "mlir/Conversion/IndexToLLVM/IndexToLLVM.h"
30#include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
31#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
32#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
33#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
34#include "mlir/Dialect/Func/IR/FuncOps.h"
35#include "mlir/Dialect/Index/IR/IndexOps.h"
36#include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
37#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
38#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
39#include "mlir/Dialect/SCF/IR/SCF.h"
40#include "mlir/IR/BuiltinDialect.h"
41#include "mlir/Pass/Pass.h"
42#include "mlir/Transforms/DialectConversion.h"
43#include "llvm/Support/Debug.h"
44#include "llvm/Support/FormatVariadic.h"
48#define DEBUG_TYPE "lower-arc-to-llvm"
51#define GEN_PASS_DEF_LOWERARCTOLLVM
52#include "circt/Conversion/Passes.h.inc"
59using namespace runtime;
66 return modelName +
"_eval";
72 using OpConversionPattern::OpConversionPattern;
74 matchAndRewrite(arc::ModelOp op, OpAdaptor adaptor,
75 ConversionPatternRewriter &rewriter)
const final {
77 IRRewriter::InsertionGuard guard(rewriter);
78 rewriter.setInsertionPointToEnd(&op.getBodyBlock());
79 func::ReturnOp::create(rewriter, op.getLoc());
84 rewriter.getFunctionType(op.getBody().getArgumentTypes(), {});
86 mlir::func::FuncOp::create(rewriter, op.getLoc(), funcName, funcType);
87 rewriter.inlineRegionBefore(op.getRegion(), func.getBody(), func.end());
93struct AllocStorageOpLowering
95 using OpConversionPattern::OpConversionPattern;
97 matchAndRewrite(arc::AllocStorageOp op, OpAdaptor adaptor,
98 ConversionPatternRewriter &rewriter)
const final {
99 auto type = typeConverter->convertType(op.getType());
100 if (!op.getOffset().has_value())
102 rewriter.replaceOpWithNewOp<LLVM::GEPOp>(op, type, rewriter.getI8Type(),
104 LLVM::GEPArg(*op.getOffset()));
109template <
class ConcreteOp>
113 using OpAdaptor =
typename ConcreteOp::Adaptor;
116 matchAndRewrite(ConcreteOp op, OpAdaptor adaptor,
117 ConversionPatternRewriter &rewriter)
const final {
119 auto offsetAttr = op->template getAttrOfType<IntegerAttr>(
"offset");
122 Value ptr = LLVM::GEPOp::create(
123 rewriter, op->getLoc(), adaptor.getStorage().getType(),
124 rewriter.getI8Type(), adaptor.getStorage(),
125 LLVM::GEPArg(offsetAttr.getValue().getZExtValue()));
126 rewriter.replaceOp(op, ptr);
132 using OpConversionPattern::OpConversionPattern;
134 matchAndRewrite(arc::StateReadOp op, OpAdaptor adaptor,
135 ConversionPatternRewriter &rewriter)
const final {
136 auto type = typeConverter->convertType(op.getType());
137 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, type, adaptor.getState());
143 using OpConversionPattern::OpConversionPattern;
145 matchAndRewrite(arc::StateWriteOp op, OpAdaptor adaptor,
146 ConversionPatternRewriter &rewriter)
const final {
147 if (adaptor.getCondition()) {
148 rewriter.replaceOpWithNewOp<scf::IfOp>(
149 op, adaptor.getCondition(), [&](
auto &builder,
auto loc) {
150 LLVM::StoreOp::create(builder, loc, adaptor.getValue(),
152 scf::YieldOp::create(builder, loc);
155 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getValue(),
167 using OpConversionPattern::OpConversionPattern;
169 matchAndRewrite(arc::CurrentTimeOp op, OpAdaptor adaptor,
170 ConversionPatternRewriter &rewriter)
const final {
172 Value ptr = adaptor.getStorage();
173 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, rewriter.getI64Type(), ptr);
180 using OpConversionPattern::OpConversionPattern;
182 matchAndRewrite(llhd::IntToTimeOp op, OpAdaptor adaptor,
183 ConversionPatternRewriter &rewriter)
const final {
184 rewriter.replaceOp(op, adaptor.getInput());
191 using OpConversionPattern::OpConversionPattern;
193 matchAndRewrite(llhd::TimeToIntOp op, OpAdaptor adaptor,
194 ConversionPatternRewriter &rewriter)
const final {
195 rewriter.replaceOp(op, adaptor.getInput());
205 using OpConversionPattern::OpConversionPattern;
207 matchAndRewrite(arc::AllocMemoryOp op, OpAdaptor adaptor,
208 ConversionPatternRewriter &rewriter)
const final {
209 auto offsetAttr = op->getAttrOfType<IntegerAttr>(
"offset");
212 Value ptr = LLVM::GEPOp::create(
213 rewriter, op.getLoc(), adaptor.getStorage().getType(),
214 rewriter.getI8Type(), adaptor.getStorage(),
215 LLVM::GEPArg(offsetAttr.getValue().getZExtValue()));
217 rewriter.replaceOp(op, ptr);
223 using OpConversionPattern::OpConversionPattern;
225 matchAndRewrite(arc::StorageGetOp op, OpAdaptor adaptor,
226 ConversionPatternRewriter &rewriter)
const final {
227 Value offset = LLVM::ConstantOp::create(
228 rewriter, op.getLoc(), rewriter.getI32Type(), op.getOffsetAttr());
229 Value ptr = LLVM::GEPOp::create(
230 rewriter, op.getLoc(), adaptor.getStorage().getType(),
231 rewriter.getI8Type(), adaptor.getStorage(), offset);
232 rewriter.replaceOp(op, ptr);
242static MemoryAccess prepareMemoryAccess(Location loc, Value memory,
243 Value address, MemoryType type,
244 ConversionPatternRewriter &rewriter) {
245 auto zextAddrType = rewriter.getIntegerType(
246 cast<IntegerType>(address.getType()).getWidth() + 1);
247 Value
addr = LLVM::ZExtOp::create(rewriter, loc, zextAddrType, address);
249 LLVM::ConstantOp::create(rewriter, loc, zextAddrType,
250 rewriter.getI32IntegerAttr(type.getNumWords()));
251 Value withinBounds = LLVM::ICmpOp::create(
252 rewriter, loc, LLVM::ICmpPredicate::ult, addr, addrLimit);
253 Value ptr = LLVM::GEPOp::create(
254 rewriter, loc, LLVM::LLVMPointerType::get(memory.getContext()),
255 rewriter.getIntegerType(type.getStride() * 8), memory, ValueRange{addr});
256 return {ptr, withinBounds};
260 using OpConversionPattern::OpConversionPattern;
262 matchAndRewrite(arc::MemoryReadOp op, OpAdaptor adaptor,
263 ConversionPatternRewriter &rewriter)
const final {
264 auto type = typeConverter->convertType(op.getType());
265 auto memoryType = cast<MemoryType>(op.getMemory().getType());
267 prepareMemoryAccess(op.getLoc(), adaptor.getMemory(),
268 adaptor.getAddress(), memoryType, rewriter);
272 rewriter.replaceOpWithNewOp<scf::IfOp>(
273 op, access.withinBounds,
274 [&](
auto &builder,
auto loc) {
275 Value loadOp = LLVM::LoadOp::create(
276 builder, loc, memoryType.getWordType(), access.ptr);
277 scf::YieldOp::create(builder, loc, loadOp);
279 [&](
auto &builder,
auto loc) {
280 Value zeroValue = LLVM::ConstantOp::create(
281 builder, loc, type, builder.getI64IntegerAttr(0));
282 scf::YieldOp::create(builder, loc, zeroValue);
289 using OpConversionPattern::OpConversionPattern;
291 matchAndRewrite(arc::MemoryWriteOp op, OpAdaptor adaptor,
292 ConversionPatternRewriter &rewriter)
const final {
293 auto access = prepareMemoryAccess(
294 op.getLoc(), adaptor.getMemory(), adaptor.getAddress(),
295 cast<MemoryType>(op.getMemory().getType()), rewriter);
296 auto enable = access.withinBounds;
297 if (adaptor.getEnable())
298 enable = LLVM::AndOp::create(rewriter, op.getLoc(), adaptor.getEnable(),
302 rewriter.replaceOpWithNewOp<scf::IfOp>(
303 op, enable, [&](
auto &builder,
auto loc) {
304 LLVM::StoreOp::create(builder, loc, adaptor.getData(), access.ptr);
305 scf::YieldOp::create(builder, loc);
313 using OpConversionPattern::OpConversionPattern;
315 matchAndRewrite(seq::ClockGateOp op, OpAdaptor adaptor,
316 ConversionPatternRewriter &rewriter)
const final {
317 rewriter.replaceOpWithNewOp<LLVM::AndOp>(op, adaptor.getInput(),
318 adaptor.getEnable());
325 using OpConversionPattern::OpConversionPattern;
327 matchAndRewrite(seq::ClockInverterOp op, OpAdaptor adaptor,
328 ConversionPatternRewriter &rewriter)
const final {
329 auto constTrue = LLVM::ConstantOp::create(rewriter, op->getLoc(),
330 rewriter.getI1Type(), 1);
331 rewriter.replaceOpWithNewOp<LLVM::XOrOp>(op, adaptor.getInput(), constTrue);
337 using OpConversionPattern::OpConversionPattern;
339 matchAndRewrite(arc::ZeroCountOp op, OpAdaptor adaptor,
340 ConversionPatternRewriter &rewriter)
const override {
342 IntegerAttr isZeroPoison = rewriter.getBoolAttr(
true);
344 if (op.getPredicate() == arc::ZeroCountPredicate::leading) {
345 rewriter.replaceOpWithNewOp<LLVM::CountLeadingZerosOp>(
346 op, adaptor.getInput().getType(), adaptor.getInput(), isZeroPoison);
350 rewriter.replaceOpWithNewOp<LLVM::CountTrailingZerosOp>(
351 op, adaptor.getInput().getType(), adaptor.getInput(), isZeroPoison);
357 using OpConversionPattern::OpConversionPattern;
359 matchAndRewrite(seq::ConstClockOp op, OpAdaptor adaptor,
360 ConversionPatternRewriter &rewriter)
const override {
361 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(
362 op, rewriter.getI1Type(),
static_cast<int64_t
>(op.getValue()));
367template <
typename OpTy>
370 using OpAdaptor =
typename OpTy::Adaptor;
372 matchAndRewrite(OpTy op, OpAdaptor adaptor,
373 ConversionPatternRewriter &rewriter)
const override {
374 rewriter.replaceOp(op, adaptor.getInput());
388 size_t numStateBytes;
389 llvm::DenseMap<StringRef, StateInfo> states;
390 mlir::FlatSymbolRefAttr initialFnSymbol;
391 mlir::FlatSymbolRefAttr finalFnSymbol;
394template <
typename OpTy>
396 ModelAwarePattern(
const TypeConverter &typeConverter, MLIRContext *
context,
397 llvm::DenseMap<StringRef, ModelInfoMap> &modelInfo)
399 modelInfo(modelInfo) {}
402 Value createPtrToPortState(ConversionPatternRewriter &rewriter, Location loc,
403 Value state,
const StateInfo &port)
const {
404 MLIRContext *ctx = rewriter.getContext();
405 return LLVM::GEPOp::create(rewriter, loc, LLVM::LLVMPointerType::get(ctx),
406 IntegerType::get(ctx, 8), state,
407 LLVM::GEPArg(port.offset));
410 llvm::DenseMap<StringRef, ModelInfoMap> &modelInfo;
415struct SimInstantiateOpLowering
416 :
public ModelAwarePattern<arc::SimInstantiateOp> {
417 using ModelAwarePattern::ModelAwarePattern;
420 matchAndRewrite(arc::SimInstantiateOp op, OpAdaptor adaptor,
421 ConversionPatternRewriter &rewriter)
const final {
422 auto modelIt = modelInfo.find(
423 cast<SimModelInstanceType>(op.getBody().getArgument(0).getType())
426 ModelInfoMap &model = modelIt->second;
428 bool useRuntime = op.getRuntimeModel().has_value();
430 ModuleOp moduleOp = op->getParentOfType<ModuleOp>();
434 ConversionPatternRewriter::InsertionGuard guard(rewriter);
438 Type convertedIndex = typeConverter->convertType(rewriter.getIndexType());
439 Location loc = op.getLoc();
444 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
448 if (op.getRuntimeArgs().has_value()) {
449 SmallVector<int8_t> argStringVec(op.getRuntimeArgsAttr().begin(),
450 op.getRuntimeArgsAttr().end());
451 argStringVec.push_back(
'\0');
452 auto strAttr = mlir::DenseElementsAttr::get(
453 mlir::RankedTensorType::get({(int64_t)argStringVec.size()},
454 rewriter.getI8Type()),
455 llvm::ArrayRef(argStringVec));
457 auto arrayCst = LLVM::ConstantOp::create(
459 LLVM::LLVMArrayType::get(rewriter.getI8Type(), argStringVec.size()),
461 auto cst1 = LLVM::ConstantOp::create(rewriter, loc,
462 rewriter.getI32IntegerAttr(1));
463 runtimeArgs = LLVM::AllocaOp::create(rewriter, loc, ptrTy,
464 arrayCst.getType(), cst1);
465 LLVM::LifetimeStartOp::create(rewriter, loc, runtimeArgs);
466 LLVM::StoreOp::create(rewriter, loc, arrayCst, runtimeArgs);
468 runtimeArgs = LLVM::ZeroOp::create(rewriter, loc, ptrTy).getResult();
471 auto rtModelPtr = LLVM::AddressOfOp::create(rewriter, loc, ptrTy,
472 op.getRuntimeModelAttr())
475 LLVM::CallOp::create(rewriter, loc, {ptrTy},
476 runtime::APICallbacks::symNameAllocInstance,
477 {rtModelPtr, runtimeArgs})
480 if (op.getRuntimeArgs().has_value())
481 LLVM::LifetimeEndOp::create(rewriter, loc, runtimeArgs);
485 FailureOr<LLVM::LLVMFuncOp> mallocFunc =
486 LLVM::lookupOrCreateMallocFn(rewriter, moduleOp, convertedIndex);
487 if (failed(mallocFunc))
490 Value numStateBytes = LLVM::ConstantOp::create(
491 rewriter, loc, convertedIndex, model.numStateBytes);
492 allocated = LLVM::CallOp::create(rewriter, loc, mallocFunc.value(),
493 ValueRange{numStateBytes})
496 LLVM::ConstantOp::create(rewriter, loc, rewriter.getI8Type(), 0);
497 LLVM::MemsetOp::create(rewriter, loc, allocated, zero, numStateBytes,
502 if (model.initialFnSymbol) {
503 auto initialFnType = LLVM::LLVMFunctionType::get(
504 LLVM::LLVMVoidType::get(op.getContext()),
505 {LLVM::LLVMPointerType::get(op.getContext())});
506 LLVM::CallOp::create(rewriter, loc, initialFnType, model.initialFnSymbol,
507 ValueRange{allocated});
512 LLVM::CallOp::create(rewriter, loc, TypeRange{},
513 runtime::APICallbacks::symNameOnInitialized,
517 rewriter.inlineBlockBefore(&adaptor.getBody().getBlocks().front(), op,
521 if (model.finalFnSymbol) {
522 auto finalFnType = LLVM::LLVMFunctionType::get(
523 LLVM::LLVMVoidType::get(op.getContext()),
524 {LLVM::LLVMPointerType::get(op.getContext())});
525 LLVM::CallOp::create(rewriter, loc, finalFnType, model.finalFnSymbol,
526 ValueRange{allocated});
530 LLVM::CallOp::create(rewriter, loc, TypeRange{},
531 runtime::APICallbacks::symNameDeleteInstance,
534 FailureOr<LLVM::LLVMFuncOp> freeFunc =
535 LLVM::lookupOrCreateFreeFn(rewriter, moduleOp);
536 if (failed(freeFunc))
539 LLVM::CallOp::create(rewriter, loc, freeFunc.value(),
540 ValueRange{allocated});
543 rewriter.eraseOp(op);
548struct SimSetInputOpLowering :
public ModelAwarePattern<arc::SimSetInputOp> {
549 using ModelAwarePattern::ModelAwarePattern;
552 matchAndRewrite(arc::SimSetInputOp op, OpAdaptor adaptor,
553 ConversionPatternRewriter &rewriter)
const final {
555 modelInfo.find(cast<SimModelInstanceType>(op.getInstance().getType())
558 ModelInfoMap &model = modelIt->second;
560 auto portIt = model.states.find(op.getInput());
561 if (portIt == model.states.end()) {
564 rewriter.eraseOp(op);
568 StateInfo &port = portIt->second;
569 Value statePtr = createPtrToPortState(rewriter, op.getLoc(),
570 adaptor.getInstance(), port);
571 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getValue(),
578struct SimGetPortOpLowering :
public ModelAwarePattern<arc::SimGetPortOp> {
579 using ModelAwarePattern::ModelAwarePattern;
582 matchAndRewrite(arc::SimGetPortOp op, OpAdaptor adaptor,
583 ConversionPatternRewriter &rewriter)
const final {
585 modelInfo.find(cast<SimModelInstanceType>(op.getInstance().getType())
588 ModelInfoMap &model = modelIt->second;
590 auto type = typeConverter->convertType(op.getValue().getType());
593 auto portIt = model.states.find(op.getPort());
594 if (portIt == model.states.end()) {
597 rewriter.replaceOpWithNewOp<LLVM::ConstantOp>(op, type, 0);
601 StateInfo &port = portIt->second;
602 Value statePtr = createPtrToPortState(rewriter, op.getLoc(),
603 adaptor.getInstance(), port);
604 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, type, statePtr);
610struct SimStepOpLowering :
public ModelAwarePattern<arc::SimStepOp> {
611 using ModelAwarePattern::ModelAwarePattern;
614 matchAndRewrite(arc::SimStepOp op, OpAdaptor adaptor,
615 ConversionPatternRewriter &rewriter)
const final {
616 StringRef modelName = cast<SimModelInstanceType>(op.getInstance().getType())
620 StringAttr evalFunc =
622 rewriter.replaceOpWithNewOp<LLVM::CallOp>(op, mlir::TypeRange(), evalFunc,
623 adaptor.getInstance());
632 using OpConversionPattern::OpConversionPattern;
635 matchAndRewrite(arc::SimGetTimeOp op, OpAdaptor adaptor,
636 ConversionPatternRewriter &rewriter)
const final {
638 rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, rewriter.getI64Type(),
639 adaptor.getInstance());
647 using OpConversionPattern::OpConversionPattern;
650 matchAndRewrite(arc::SimSetTimeOp op, OpAdaptor adaptor,
651 ConversionPatternRewriter &rewriter)
const final {
653 rewriter.replaceOpWithNewOp<LLVM::StoreOp>(op, adaptor.getTime(),
654 adaptor.getInstance());
662 Value getOrCreate(OpBuilder &b, StringRef formatStr) {
663 auto it = cache.find(formatStr);
664 if (it != cache.end()) {
665 return LLVM::AddressOfOp::create(b,
b.getUnknownLoc(), it->second);
668 Location loc =
b.getUnknownLoc();
669 LLVM::GlobalOp global;
671 OpBuilder::InsertionGuard guard(b);
673 b.getInsertionBlock()->getParent()->getParentOfType<ModuleOp>();
674 b.setInsertionPointToStart(m.getBody());
676 SmallVector<char> strVec(formatStr.begin(), formatStr.end());
679 auto name = llvm::formatv(
"_arc_str_{0}", cache.size()).str();
680 auto globalType = LLVM::LLVMArrayType::get(
b.getI8Type(), strVec.size());
681 global = LLVM::GlobalOp::create(b, loc, globalType,
true,
682 LLVM::Linkage::Internal,
683 name,
b.getStringAttr(strVec),
687 cache[formatStr] = global;
688 return LLVM::AddressOfOp::create(b, loc, global);
692 llvm::StringMap<LLVM::GlobalOp> cache;
695FailureOr<LLVM::CallOp> emitPrintfCall(OpBuilder &builder, Location loc,
696 StringCache &cache, StringRef formatStr,
699 builder.getInsertionBlock()->getParent()->getParentOfType<ModuleOp>();
701 MLIRContext *ctx = builder.getContext();
702 auto printfFunc = LLVM::lookupOrCreateFn(builder, moduleOp,
"printf",
703 LLVM::LLVMPointerType::get(ctx),
704 LLVM::LLVMVoidType::get(ctx),
true);
705 if (failed(printfFunc))
708 Value formatStrPtr = cache.getOrCreate(builder, formatStr);
709 SmallVector<Value> argsVec(1, formatStrPtr);
710 argsVec.append(args.begin(), args.end());
711 return LLVM::CallOp::create(builder, loc, printfFunc.value(), argsVec);
717struct SimEmitValueOpLowering
719 SimEmitValueOpLowering(
const TypeConverter &typeConverter,
720 MLIRContext *
context, StringCache &formatStringCache)
722 formatStringCache(formatStringCache) {}
725 matchAndRewrite(arc::SimEmitValueOp op, OpAdaptor adaptor,
726 ConversionPatternRewriter &rewriter)
const final {
727 auto valueType = dyn_cast<IntegerType>(adaptor.getValue().getType());
731 Location loc = op.getLoc();
733 ModuleOp moduleOp = op->getParentOfType<ModuleOp>();
737 SmallVector<Value> printfVariadicArgs;
738 SmallString<16> printfFormatStr;
739 int remainingBits = valueType.getWidth();
740 Value value = adaptor.getValue();
744 constexpr llvm::StringRef intFormatter =
"llx";
745 auto intType = IntegerType::get(getContext(), 64);
746 Value shiftValue = LLVM::ConstantOp::create(
747 rewriter, loc, rewriter.getIntegerAttr(valueType, intType.getWidth()));
749 if (valueType.getWidth() < intType.getWidth()) {
750 int width = llvm::divideCeil(valueType.getWidth(), 4);
751 printfFormatStr = llvm::formatv(
"%0{0}{1}", width, intFormatter);
752 printfVariadicArgs.push_back(
753 LLVM::ZExtOp::create(rewriter, loc, intType, value));
758 int otherChunkWidth = intType.getWidth() / 4;
759 int firstChunkWidth =
760 llvm::divideCeil(valueType.getWidth() % intType.getWidth(), 4);
761 if (firstChunkWidth == 0) {
762 firstChunkWidth = otherChunkWidth;
765 std::string firstChunkFormat =
766 llvm::formatv(
"%0{0}{1}", firstChunkWidth, intFormatter);
767 std::string otherChunkFormat =
768 llvm::formatv(
"%0{0}{1}", otherChunkWidth, intFormatter);
770 for (
int i = 0; remainingBits > 0; ++i) {
773 printfVariadicArgs.push_back(
774 LLVM::TruncOp::create(rewriter, loc, intType, value));
777 printfFormatStr.append(i == 0 ? firstChunkFormat : otherChunkFormat);
780 LLVM::LShrOp::create(rewriter, loc, value, shiftValue).getResult();
781 remainingBits -= intType.getWidth();
785 std::reverse(printfVariadicArgs.begin(), printfVariadicArgs.end());
787 SmallString<16> formatStr = adaptor.getValueName();
788 formatStr.append(
" = ");
789 formatStr.append(printfFormatStr);
790 formatStr.append(
"\n");
792 auto callOp = emitPrintfCall(rewriter, op->getLoc(), formatStringCache,
793 formatStr, printfVariadicArgs);
796 rewriter.replaceOp(op, *callOp);
801 StringCache &formatStringCache;
810 SmallVector<FmtDescriptor> descriptors;
811 SmallVector<Value> args;
818static Value reg2mem(ConversionPatternRewriter &rewriter, Location loc,
821 int64_t origBitwidth = cast<IntegerType>(value.getType()).getWidth();
822 int64_t bitwidth = llvm::divideCeil(origBitwidth, 64) * 64;
823 int64_t numWords = bitwidth / 64;
826 LLVM::ConstantOp alloca_size =
827 LLVM::ConstantOp::create(rewriter, loc, rewriter.getI32Type(), numWords);
828 auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext());
829 auto allocaOp = LLVM::AllocaOp::create(rewriter, loc, ptrType,
830 rewriter.getI64Type(), alloca_size);
831 LLVM::LifetimeStartOp::create(rewriter, loc, allocaOp);
835 for (int64_t wordIdx = 0; wordIdx < numWords; ++wordIdx) {
836 Value cst = LLVM::ConstantOp::create(
837 rewriter, loc, rewriter.getIntegerType(origBitwidth), wordIdx * 64);
838 Value v = LLVM::LShrOp::create(rewriter, loc, value, cst);
839 if (origBitwidth > 64) {
840 v = LLVM::TruncOp::create(rewriter, loc, rewriter.getI64Type(), v);
841 }
else if (origBitwidth < 64) {
842 v = LLVM::ZExtOp::create(rewriter, loc, rewriter.getI64Type(), v);
844 Value gep = LLVM::GEPOp::create(rewriter, loc, ptrType,
845 rewriter.getI64Type(), allocaOp, {wordIdx});
846 LLVM::StoreOp::create(rewriter, loc, v, gep);
853static FailureOr<FormatInfo>
854foldFormatString(ConversionPatternRewriter &rewriter, Value fstringValue,
855 StringCache &cache) {
856 Operation *op = fstringValue.getDefiningOp();
857 return llvm::TypeSwitch<Operation *, FailureOr<FormatInfo>>(op)
858 .Case<sim::FormatCharOp>(
859 [&](sim::FormatCharOp op) -> FailureOr<FormatInfo> {
860 FmtDescriptor
d = FmtDescriptor::createChar();
861 return FormatInfo{{
d}, {op.getValue()}};
863 .Case<sim::FormatDecOp>([&](sim::FormatDecOp op)
864 -> FailureOr<FormatInfo> {
865 FmtDescriptor
d = FmtDescriptor::createInt(
866 op.getValue().getType().getWidth(), 10, op.getIsLeftAligned(),
867 op.getSpecifierWidth().value_or(-1),
false, op.getIsSigned());
868 return FormatInfo{{
d}, {reg2mem(rewriter, op.getLoc(), op.getValue())}};
870 .Case<sim::FormatHexOp>([&](sim::FormatHexOp op)
871 -> FailureOr<FormatInfo> {
872 FmtDescriptor
d = FmtDescriptor::createInt(
873 op.getValue().getType().getWidth(), 16, op.getIsLeftAligned(),
874 op.getSpecifierWidth().value_or(-1), op.getIsHexUppercase(),
false);
875 return FormatInfo{{
d}, {reg2mem(rewriter, op.getLoc(), op.getValue())}};
877 .Case<sim::FormatOctOp>([&](sim::FormatOctOp op)
878 -> FailureOr<FormatInfo> {
879 FmtDescriptor
d = FmtDescriptor::createInt(
880 op.getValue().getType().getWidth(), 8, op.getIsLeftAligned(),
881 op.getSpecifierWidth().value_or(-1),
false,
false);
882 return FormatInfo{{
d}, {reg2mem(rewriter, op.getLoc(), op.getValue())}};
884 .Case<sim::FormatLiteralOp>(
885 [&](sim::FormatLiteralOp op) -> FailureOr<FormatInfo> {
886 if (op.getLiteral().size() < 8 &&
887 op.getLiteral().find(
'\0') == StringRef::npos) {
890 FmtDescriptor::createSmallLiteral(op.getLiteral());
891 return FormatInfo{{
d}, {}};
894 FmtDescriptor::createLiteral(op.getLiteral().size());
895 Value value = cache.getOrCreate(rewriter, op.getLiteral());
896 return FormatInfo{{
d}, {value}};
898 .Case<sim::FormatStringConcatOp>(
899 [&](sim::FormatStringConcatOp op) -> FailureOr<FormatInfo> {
900 auto fmt = foldFormatString(rewriter, op.getInputs()[0], cache);
903 for (
auto input : op.getInputs().drop_front()) {
904 auto next = foldFormatString(rewriter, input, cache);
907 fmt->descriptors.append(next->descriptors);
908 fmt->args.append(next->args);
913 [](Operation *op) -> FailureOr<FormatInfo> {
return failure(); });
916FailureOr<LLVM::CallOp> emitFmtCall(OpBuilder &builder, Location loc,
917 StringCache &stringCache,
918 ArrayRef<FmtDescriptor> descriptors,
921 builder.getInsertionBlock()->getParent()->getParentOfType<ModuleOp>();
923 MLIRContext *ctx = builder.getContext();
924 auto func = LLVM::lookupOrCreateFn(
925 builder, moduleOp, runtime::APICallbacks::symNameFormat,
926 LLVM::LLVMPointerType::get(ctx), LLVM::LLVMVoidType::get(ctx),
true);
930 StringRef rawDescriptors(
reinterpret_cast<const char *
>(descriptors.data()),
931 descriptors.size() *
sizeof(FmtDescriptor));
932 Value fmtPtr = stringCache.getOrCreate(builder, rawDescriptors);
934 SmallVector<Value> argsVec(1, fmtPtr);
935 argsVec.append(args.begin(), args.end());
936 auto result = LLVM::CallOp::create(builder, loc, func.value(), argsVec);
938 for (Value arg : args) {
939 Operation *definingOp = arg.getDefiningOp();
940 if (
auto alloca = dyn_cast_if_present<LLVM::AllocaOp>(definingOp)) {
941 LLVM::LifetimeEndOp::create(builder, loc, arg);
948struct SimPrintFormattedProcOpLowering
950 SimPrintFormattedProcOpLowering(
const TypeConverter &typeConverter,
952 StringCache &stringCache)
954 stringCache(stringCache) {}
957 matchAndRewrite(sim::PrintFormattedProcOp op, OpAdaptor adaptor,
958 ConversionPatternRewriter &rewriter)
const override {
959 auto formatInfo = foldFormatString(rewriter, op.getInput(), stringCache);
960 if (failed(formatInfo))
961 return rewriter.notifyMatchFailure(op,
"unsupported format string");
964 formatInfo->descriptors.push_back(FmtDescriptor());
966 auto result = emitFmtCall(rewriter, op.getLoc(), stringCache,
967 formatInfo->descriptors, formatInfo->args);
970 rewriter.replaceOp(op, result.value());
975 StringCache &stringCache;
980static LogicalResult
convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor,
981 ConversionPatternRewriter &rewriter,
982 const TypeConverter &converter) {
984 if (failed(rewriter.convertRegionTypes(&op.getBody(), converter)))
990 auto *blockBefore = rewriter.getInsertionBlock();
992 rewriter.splitBlock(blockBefore, rewriter.getInsertionPoint());
995 rewriter.setInsertionPointToEnd(blockBefore);
996 mlir::cf::BranchOp::create(rewriter, op.getLoc(), &op.getBody().front(),
997 adaptor.getInputs());
1001 for (
auto &block : op.getBody()) {
1002 auto outputOp = dyn_cast<arc::OutputOp>(block.getTerminator());
1005 rewriter.setInsertionPointToEnd(&block);
1006 rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(outputOp, blockAfter,
1007 outputOp.getOperands());
1011 rewriter.inlineRegionBefore(op.getBody(), blockAfter);
1015 SmallVector<Value> args;
1016 args.reserve(op.getNumResults());
1017 for (
auto result : op.getResults())
1018 args.push_back(blockAfter->addArgument(result.getType(), result.getLoc()));
1019 rewriter.replaceOp(op, args);
1020 auto conversion = converter.convertBlockSignature(blockAfter);
1023 rewriter.applySignatureConversion(blockAfter, *conversion, &converter);
1031template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
1032static LLVM::GlobalOp
1034 SmallVectorImpl<T> &data,
1035 unsigned alignment =
alignof(T)) {
1036 auto intType = builder.getIntegerType(8 *
sizeof(T));
1037 Attribute denseAttr = mlir::DenseElementsAttr::get(
1038 mlir::RankedTensorType::get({(int64_t)data.size()}, intType),
1039 llvm::ArrayRef(data));
1040 auto globalOp = LLVM::GlobalOp::create(
1041 builder, loc, LLVM::LLVMArrayType::get(intType, data.size()),
1042 true, LLVM::Linkage::Internal,
1043 builder.getStringAttr(symName), denseAttr);
1044 globalOp.setAlignmentAttr(builder.getI64IntegerAttr(alignment));
1049template <
typename T>
1050static LLVM::GlobalOp
1053 SmallVectorImpl<T> &array) {
1055 static_assert(std::is_standard_layout<T>(),
1056 "Runtime struct must have standard layout");
1057 int64_t numBytes =
sizeof(T) * array.size();
1058 Attribute denseAttr = mlir::DenseElementsAttr::get(
1059 mlir::RankedTensorType::get({numBytes}, builder.getI8Type()),
1060 llvm::ArrayRef(
reinterpret_cast<uint8_t *
>(array.data()), numBytes));
1061 auto globalOp = LLVM::GlobalOp::create(
1062 builder, loc, LLVM::LLVMArrayType::get(builder.getI8Type(), numBytes),
1063 true, LLVM::Linkage::Internal,
1064 builder.getStringAttr(symName), denseAttr,
alignof(T));
1070 using OpConversionPattern::OpConversionPattern;
1077 ConversionPatternRewriter &rewriter)
const {
1078 if (!op.getTraceTaps().has_value() || op.getTraceTaps()->empty())
1081 SmallVector<char> namesArray;
1082 SmallVector<ArcTraceTap> tapArray;
1083 tapArray.reserve(op.getTraceTaps()->size());
1084 for (
auto attr : op.getTraceTapsAttr()) {
1085 auto tap = cast<TraceTapAttr>(attr);
1086 assert(!tap.getNames().empty() &&
1087 "Expected trace tap to have at least one name");
1088 for (
auto alias : tap.getNames()) {
1089 auto aliasStr = cast<StringAttr>(alias);
1090 namesArray.append(aliasStr.begin(), aliasStr.end());
1091 namesArray.push_back(
'\0');
1095 tapStruct.
nameOffset = namesArray.size() - 1;
1096 tapStruct.
typeBits = tap.getSigType().getValue().getIntOrFloatBitWidth();
1098 tapArray.emplace_back(tapStruct);
1100 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1102 rewriter, op.getLoc(),
"_arc_tap_names_" + op.getName(), namesArray);
1104 rewriter, op.getLoc(),
"_arc_trace_taps_" + op.getName(), tapArray);
1114 auto traceInfoStructType = LLVM::LLVMStructType::getLiteral(
1116 {rewriter.getI64Type(), ptrTy, ptrTy, rewriter.getI64Type()});
1118 "Unexpected size of ArcModelTraceInfo struct");
1120 auto globalSymName =
1121 rewriter.getStringAttr(
"_arc_trace_info_" + op.getName());
1122 auto traceInfoGlobalOp = LLVM::GlobalOp::create(
1123 rewriter, op.getLoc(), traceInfoStructType,
1124 false, LLVM::Linkage::Internal, globalSymName,
1126 OpBuilder::InsertionGuard g(rewriter);
1129 Region &initRegion = traceInfoGlobalOp.getInitializerRegion();
1130 Block *initBlock = rewriter.createBlock(&initRegion);
1131 rewriter.setInsertionPointToStart(initBlock);
1133 auto numTraceTapsCst = LLVM::ConstantOp::create(
1134 rewriter, op.getLoc(), rewriter.getI64IntegerAttr(tapArray.size()));
1135 auto traceTapArrayAddr =
1136 LLVM::AddressOfOp::create(rewriter, op.getLoc(), traceTapsArrayGlobal);
1137 auto tapNameArrayAddr =
1138 LLVM::AddressOfOp::create(rewriter, op.getLoc(), namesGlobal);
1139 auto bufferCapacityCst = LLVM::ConstantOp::create(
1140 rewriter, op.getLoc(),
1141 rewriter.getI64IntegerAttr(runtime::defaultTraceBufferCapacity));
1144 LLVM::PoisonOp::create(rewriter, op.getLoc(), traceInfoStructType);
1148 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1149 numTraceTapsCst, ArrayRef<int64_t>{0});
1151 "Unexpected offset of field numTraceTaps");
1154 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1155 traceTapArrayAddr, ArrayRef<int64_t>{1});
1157 "Unexpected offset of field traceTaps");
1160 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1161 tapNameArrayAddr, ArrayRef<int64_t>{2});
1163 "Unexpected offset of field traceTapNames");
1166 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1167 bufferCapacityCst, ArrayRef<int64_t>{3});
1169 "Unexpected offset of field traceBufferCapacity");
1170 LLVM::ReturnOp::create(rewriter, op.getLoc(), initStruct);
1172 return traceInfoGlobalOp;
1178 ConversionPatternRewriter &rewriter)
const final {
1180 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
1181 auto modelInfoStructType = LLVM::LLVMStructType::getLiteral(
1183 {rewriter.getI64Type(), rewriter.getI64Type(), ptrTy, ptrTy});
1185 "Unexpected size of ArcRuntimeModelInfo struct");
1187 rewriter.setInsertionPoint(op);
1191 SmallVector<char, 16> modNameArray(op.getName().begin(),
1192 op.getName().end());
1193 modNameArray.push_back(
'\0');
1194 auto nameGlobalType =
1195 LLVM::LLVMArrayType::get(rewriter.getI8Type(), modNameArray.size());
1196 auto globalSymName =
1197 rewriter.getStringAttr(
"_arc_mod_name_" + op.getName());
1198 auto nameGlobal = LLVM::GlobalOp::create(
1199 rewriter, op.getLoc(), nameGlobalType,
true,
1200 LLVM::Linkage::Internal,
1201 globalSymName, rewriter.getStringAttr(modNameArray),
1208 auto modInfoGlobalOp =
1209 LLVM::GlobalOp::create(rewriter, op.getLoc(), modelInfoStructType,
1210 false, LLVM::Linkage::External,
1211 op.getSymName(), Attribute{});
1214 Region &initRegion = modInfoGlobalOp.getInitializerRegion();
1215 Block *initBlock = rewriter.createBlock(&initRegion);
1216 rewriter.setInsertionPointToStart(initBlock);
1217 auto apiVersionCst = LLVM::ConstantOp::create(
1219 auto numStateBytesCst = LLVM::ConstantOp::create(rewriter, op.getLoc(),
1220 op.getNumStateBytesAttr());
1222 LLVM::AddressOfOp::create(rewriter, op.getLoc(), nameGlobal);
1224 if (traceInfoGlobal)
1226 LLVM::AddressOfOp::create(rewriter, op.getLoc(), traceInfoGlobal);
1228 traceInfoPtr = LLVM::ZeroOp::create(rewriter, op.getLoc(), ptrTy);
1231 LLVM::PoisonOp::create(rewriter, op.getLoc(), modelInfoStructType);
1234 initStruct = LLVM::InsertValueOp::create(
1235 rewriter, op.getLoc(), initStruct, apiVersionCst, ArrayRef<int64_t>{0});
1237 "Unexpected offset of field apiVersion");
1240 LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1241 numStateBytesCst, ArrayRef<int64_t>{1});
1243 "Unexpected offset of field numStateBytes");
1245 initStruct = LLVM::InsertValueOp::create(rewriter, op.getLoc(), initStruct,
1246 nameAddr, ArrayRef<int64_t>{2});
1248 "Unexpected offset of field modelName");
1250 initStruct = LLVM::InsertValueOp::create(
1251 rewriter, op.getLoc(), initStruct, traceInfoPtr, ArrayRef<int64_t>{3});
1253 "Unexpected offset of field traceInfo");
1255 LLVM::ReturnOp::create(rewriter, op.getLoc(), initStruct);
1257 rewriter.replaceOp(op, modInfoGlobalOp);
1267struct LowerArcToLLVMPass
1268 :
public circt::impl::LowerArcToLLVMBase<LowerArcToLLVMPass> {
1269 void runOnOperation()
override;
1273void LowerArcToLLVMPass::runOnOperation() {
1277 DenseMap<Region *, hw::ConstantOp> zeros;
1278 getOperation().walk([&](Operation *op) {
1279 if (op->hasTrait<OpTrait::ConstantLike>())
1281 for (
auto result : op->getResults()) {
1282 auto type = dyn_cast<IntegerType>(result.getType());
1283 if (!type || type.getWidth() != 0)
1285 auto *region = op->getParentRegion();
1286 auto &zero = zeros[region];
1288 auto builder = OpBuilder::atBlockBegin(®ion->front());
1292 result.replaceAllUsesWith(zero);
1308 LLVMConversionTarget target(getContext());
1309 target.addLegalOp<mlir::ModuleOp>();
1310 target.addLegalOp<scf::YieldOp>();
1315 target.addLegalOp<sim::FormatLiteralOp, sim::FormatDecOp, sim::FormatHexOp,
1316 sim::FormatBinOp, sim::FormatOctOp, sim::FormatCharOp,
1317 sim::FormatStringConcatOp>();
1320 LLVMTypeConverter converter(&getContext());
1321 converter.addConversion([&](seq::ClockType type) {
1322 return IntegerType::get(type.getContext(), 1);
1324 converter.addConversion([&](StorageType type) {
1325 return LLVM::LLVMPointerType::get(type.getContext());
1327 converter.addConversion([&](MemoryType type) {
1328 return LLVM::LLVMPointerType::get(type.getContext());
1330 converter.addConversion([&](StateType type) {
1331 return LLVM::LLVMPointerType::get(type.getContext());
1333 converter.addConversion([&](SimModelInstanceType type) {
1334 return LLVM::LLVMPointerType::get(type.getContext());
1336 converter.addConversion([&](sim::FormatStringType type) {
1337 return LLVM::LLVMPointerType::get(type.getContext());
1339 converter.addConversion([&](llhd::TimeType type) {
1341 return IntegerType::get(type.getContext(), 64);
1348 populateSCFToControlFlowConversionPatterns(
patterns);
1349 populateFuncToLLVMConversionPatterns(converter,
patterns);
1350 cf::populateControlFlowToLLVMConversionPatterns(converter,
patterns);
1351 arith::populateArithToLLVMConversionPatterns(converter,
patterns);
1352 index::populateIndexToLLVMConversionPatterns(converter,
patterns);
1353 populateAnyFunctionOpInterfaceTypeConversionPattern(
patterns, converter);
1356 DenseMap<std::pair<Type, ArrayAttr>, LLVM::GlobalOp> constAggregateGlobalsMap;
1358 std::optional<HWToLLVMArraySpillCache> spillCacheOpt =
1361 OpBuilder spillBuilder(getOperation());
1362 spillCacheOpt->spillNonHWOps(spillBuilder, converter, getOperation());
1365 constAggregateGlobalsMap, spillCacheOpt);
1373 AllocMemoryOpLowering,
1374 AllocStateLikeOpLowering<arc::AllocStateOp>,
1375 AllocStateLikeOpLowering<arc::RootInputOp>,
1376 AllocStateLikeOpLowering<arc::RootOutputOp>,
1377 AllocStorageOpLowering,
1378 ClockGateOpLowering,
1380 CurrentTimeOpLowering,
1381 IntToTimeOpLowering,
1382 MemoryReadOpLowering,
1383 MemoryWriteOpLowering,
1385 ReplaceOpWithInputPattern<seq::ToClockOp>,
1386 ReplaceOpWithInputPattern<seq::FromClockOp>,
1388 SeqConstClockLowering,
1389 SimGetTimeOpLowering,
1390 SimSetTimeOpLowering,
1391 StateReadOpLowering,
1392 StateWriteOpLowering,
1393 StorageGetOpLowering,
1394 TimeToIntOpLowering,
1396 >(converter, &getContext());
1400 StringCache stringCache;
1401 patterns.add<SimEmitValueOpLowering, SimPrintFormattedProcOpLowering>(
1402 converter, &getContext(), stringCache);
1404 auto &modelInfo = getAnalysis<ModelInfoAnalysis>();
1405 llvm::DenseMap<StringRef, ModelInfoMap> modelMap(modelInfo.infoMap.size());
1406 for (
auto &[_, modelInfo] : modelInfo.infoMap) {
1407 llvm::DenseMap<StringRef, StateInfo> states(modelInfo.states.size());
1408 for (StateInfo &stateInfo : modelInfo.states)
1409 states.insert({stateInfo.name, stateInfo});
1412 ModelInfoMap{modelInfo.numStateBytes, std::move(states),
1413 modelInfo.initialFnSym, modelInfo.finalFnSym}});
1416 patterns.add<SimInstantiateOpLowering, SimSetInputOpLowering,
1417 SimGetPortOpLowering, SimStepOpLowering>(
1418 converter, &getContext(), modelMap);
1421 ConversionConfig config;
1422 config.allowPatternRollback =
false;
1423 if (failed(applyFullConversion(getOperation(), target, std::move(
patterns),
1425 signalPassFailure();
1429 return std::make_unique<LowerArcToLLVMPass>();
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static LLVM::GlobalOp buildGlobalConstantIntArray(OpBuilder &builder, Location loc, Twine symName, SmallVectorImpl< T > &data, unsigned alignment=alignof(T))
static LLVM::GlobalOp buildGlobalConstantRuntimeStructArray(OpBuilder &builder, Location loc, Twine symName, SmallVectorImpl< T > &array)
static llvm::Twine evalSymbolFromModelName(StringRef modelName)
static LogicalResult convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor, ConversionPatternRewriter &rewriter, const TypeConverter &converter)
Extension of RewritePatternSet that allows adding matchAndRewrite functions with op adaptors and Conv...
A namespace that is used to store existing names and generate new names in some scope within the IR.
void add(mlir::ModuleOp module)
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
#define ARC_RUNTIME_API_VERSION
Version of the combined public and internal API.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void populateCombToArithConversionPatterns(TypeConverter &converter, RewritePatternSet &patterns)
void populateCombToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, RewritePatternSet &patterns)
Get the Comb to LLVM conversion patterns.
void populateHWToLLVMTypeConversions(mlir::LLVMTypeConverter &converter)
Get the HW to LLVM type conversions.
void populateHWToLLVMConversionPatterns(mlir::LLVMTypeConverter &converter, RewritePatternSet &patterns, Namespace &globals, DenseMap< std::pair< Type, ArrayAttr >, mlir::LLVM::GlobalOp > &constAggregateGlobalsMap, std::optional< HWToLLVMArraySpillCache > &spillCacheOpt)
Get the HW to LLVM conversion patterns.
std::unique_ptr< OperationPass< ModuleOp > > createLowerArcToLLVMPass()
Static information for a compiled hardware model, generated by the MLIR lowering.
uint32_t typeBits
Bit width of the traced signal.
uint64_t stateOffset
Byte offset of the traced value within the model state.
uint64_t nameOffset
Byte offset to the null terminator of this signal's last alias in the names array.
uint32_t reserved
Padding and reserved for future use.
LLVM::GlobalOp buildTraceInfoStruct(arc::RuntimeModelOp &op, ConversionPatternRewriter &rewriter) const
static constexpr uint64_t runtimeApiVersion
LogicalResult matchAndRewrite(arc::RuntimeModelOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const final
Helper class mapping array values (HW or LLVM Dialect) to pointers to buffers containing the array va...