//===- AVR.cpp ------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "ABIInfoImpl.h" #include "TargetInfo.h" #include "clang/Basic/DiagnosticFrontend.h" using namespace clang; using namespace clang::CodeGen; //===----------------------------------------------------------------------===// // AVR ABI Implementation. Documented at // https://gcc.gnu.org/wiki/avr-gcc#Calling_Convention // https://gcc.gnu.org/wiki/avr-gcc#Reduced_Tiny //===----------------------------------------------------------------------===// namespace { class AVRABIInfo : public DefaultABIInfo { private: // The total amount of registers can be used to pass parameters. It is 18 on // AVR, or 6 on AVRTiny. const unsigned ParamRegs; // The total amount of registers can be used to pass return value. It is 8 on // AVR, or 4 on AVRTiny. const unsigned RetRegs; public: AVRABIInfo(CodeGenTypes &CGT, unsigned NPR, unsigned NRR) : DefaultABIInfo(CGT), ParamRegs(NPR), RetRegs(NRR) {} ABIArgInfo classifyReturnType(QualType Ty, bool &LargeRet) const { // On AVR, a return struct with size less than or equals to 8 bytes is // returned directly via registers R18-R25. On AVRTiny, a return struct // with size less than or equals to 4 bytes is returned directly via // registers R22-R25. if (isAggregateTypeForABI(Ty) && getContext().getTypeSize(Ty) <= RetRegs * 8) return ABIArgInfo::getDirect(); // A return value (struct or scalar) with larger size is returned via a // stack slot, along with a pointer as the function's implicit argument. if (getContext().getTypeSize(Ty) > RetRegs * 8) { LargeRet = true; return getNaturalAlignIndirect(Ty); } // An i8 return value should not be extended to i16, since AVR has 8-bit // registers. if (Ty->isIntegralOrEnumerationType() && getContext().getTypeSize(Ty) <= 8) return ABIArgInfo::getDirect(); // Otherwise we follow the default way which is compatible. return DefaultABIInfo::classifyReturnType(Ty); } ABIArgInfo classifyArgumentType(QualType Ty, unsigned &NumRegs) const { unsigned TySize = getContext().getTypeSize(Ty); // An int8 type argument always costs two registers like an int16. if (TySize == 8 && NumRegs >= 2) { NumRegs -= 2; return ABIArgInfo::getExtend(Ty); } // If the argument size is an odd number of bytes, round up the size // to the next even number. TySize = llvm::alignTo(TySize, 16); // Any type including an array/struct type can be passed in rgisters, // if there are enough registers left. if (TySize <= NumRegs * 8) { NumRegs -= TySize / 8; return ABIArgInfo::getDirect(); } // An argument is passed either completely in registers or completely in // memory. Since there are not enough registers left, current argument // and all other unprocessed arguments should be passed in memory. // However we still need to return `ABIArgInfo::getDirect()` other than // `ABIInfo::getNaturalAlignIndirect(Ty)`, otherwise an extra stack slot // will be allocated, so the stack frame layout will be incompatible with // avr-gcc. NumRegs = 0; return ABIArgInfo::getDirect(); } void computeInfo(CGFunctionInfo &FI) const override { // Decide the return type. bool LargeRet = false; if (!getCXXABI().classifyReturnType(FI)) FI.getReturnInfo() = classifyReturnType(FI.getReturnType(), LargeRet); // Decide each argument type. The total number of registers can be used for // arguments depends on several factors: // 1. Arguments of varargs functions are passed on the stack. This applies // even to the named arguments. So no register can be used. // 2. Total 18 registers can be used on avr and 6 ones on avrtiny. // 3. If the return type is a struct with too large size, two registers // (out of 18/6) will be cost as an implicit pointer argument. unsigned NumRegs = ParamRegs; if (FI.isVariadic()) NumRegs = 0; else if (LargeRet) NumRegs -= 2; for (auto &I : FI.arguments()) I.info = classifyArgumentType(I.type, NumRegs); } }; class AVRTargetCodeGenInfo : public TargetCodeGenInfo { public: AVRTargetCodeGenInfo(CodeGenTypes &CGT, unsigned NPR, unsigned NRR) : TargetCodeGenInfo(std::make_unique(CGT, NPR, NRR)) {} LangAS getGlobalVarAddressSpace(CodeGenModule &CGM, const VarDecl *D) const override { // Check if global/static variable is defined in address space // 1~6 (__flash, __flash1, __flash2, __flash3, __flash4, __flash5) // but not constant. if (D) { LangAS AS = D->getType().getAddressSpace(); if (isTargetAddressSpace(AS) && 1 <= toTargetAddressSpace(AS) && toTargetAddressSpace(AS) <= 6 && !D->getType().isConstQualified()) CGM.getDiags().Report(D->getLocation(), diag::err_verify_nonconst_addrspace) << "__flash*"; } return TargetCodeGenInfo::getGlobalVarAddressSpace(CGM, D); } void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &CGM) const override { if (GV->isDeclaration()) return; const auto *FD = dyn_cast_or_null(D); if (!FD) return; auto *Fn = cast(GV); if (FD->getAttr()) Fn->addFnAttr("interrupt"); if (FD->getAttr()) Fn->addFnAttr("signal"); } }; } std::unique_ptr CodeGen::createAVRTargetCodeGenInfo(CodeGenModule &CGM, unsigned NPR, unsigned NRR) { return std::make_unique(CGM.getTypes(), NPR, NRR); }