/src/llvm-project/clang/lib/CodeGen/Targets/Mips.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- Mips.cpp -----------------------------------------------------------===// |
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 "ABIInfoImpl.h" |
10 | | #include "TargetInfo.h" |
11 | | |
12 | | using namespace clang; |
13 | | using namespace clang::CodeGen; |
14 | | |
15 | | //===----------------------------------------------------------------------===// |
16 | | // MIPS ABI Implementation. This works for both little-endian and |
17 | | // big-endian variants. |
18 | | //===----------------------------------------------------------------------===// |
19 | | |
20 | | namespace { |
21 | | class MipsABIInfo : public ABIInfo { |
22 | | bool IsO32; |
23 | | const unsigned MinABIStackAlignInBytes, StackAlignInBytes; |
24 | | void CoerceToIntArgs(uint64_t TySize, |
25 | | SmallVectorImpl<llvm::Type *> &ArgList) const; |
26 | | llvm::Type* HandleAggregates(QualType Ty, uint64_t TySize) const; |
27 | | llvm::Type* returnAggregateInRegs(QualType RetTy, uint64_t Size) const; |
28 | | llvm::Type* getPaddingType(uint64_t Align, uint64_t Offset) const; |
29 | | public: |
30 | | MipsABIInfo(CodeGenTypes &CGT, bool _IsO32) : |
31 | | ABIInfo(CGT), IsO32(_IsO32), MinABIStackAlignInBytes(IsO32 ? 4 : 8), |
32 | 0 | StackAlignInBytes(IsO32 ? 8 : 16) {} |
33 | | |
34 | | ABIArgInfo classifyReturnType(QualType RetTy) const; |
35 | | ABIArgInfo classifyArgumentType(QualType RetTy, uint64_t &Offset) const; |
36 | | void computeInfo(CGFunctionInfo &FI) const override; |
37 | | Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, |
38 | | QualType Ty) const override; |
39 | | ABIArgInfo extendType(QualType Ty) const; |
40 | | }; |
41 | | |
42 | | class MIPSTargetCodeGenInfo : public TargetCodeGenInfo { |
43 | | unsigned SizeOfUnwindException; |
44 | | public: |
45 | | MIPSTargetCodeGenInfo(CodeGenTypes &CGT, bool IsO32) |
46 | | : TargetCodeGenInfo(std::make_unique<MipsABIInfo>(CGT, IsO32)), |
47 | 0 | SizeOfUnwindException(IsO32 ? 24 : 32) {} |
48 | | |
49 | 0 | int getDwarfEHStackPointer(CodeGen::CodeGenModule &CGM) const override { |
50 | 0 | return 29; |
51 | 0 | } |
52 | | |
53 | | void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, |
54 | 0 | CodeGen::CodeGenModule &CGM) const override { |
55 | 0 | const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D); |
56 | 0 | if (!FD) return; |
57 | 0 | llvm::Function *Fn = cast<llvm::Function>(GV); |
58 | |
|
59 | 0 | if (FD->hasAttr<MipsLongCallAttr>()) |
60 | 0 | Fn->addFnAttr("long-call"); |
61 | 0 | else if (FD->hasAttr<MipsShortCallAttr>()) |
62 | 0 | Fn->addFnAttr("short-call"); |
63 | | |
64 | | // Other attributes do not have a meaning for declarations. |
65 | 0 | if (GV->isDeclaration()) |
66 | 0 | return; |
67 | | |
68 | 0 | if (FD->hasAttr<Mips16Attr>()) { |
69 | 0 | Fn->addFnAttr("mips16"); |
70 | 0 | } |
71 | 0 | else if (FD->hasAttr<NoMips16Attr>()) { |
72 | 0 | Fn->addFnAttr("nomips16"); |
73 | 0 | } |
74 | |
|
75 | 0 | if (FD->hasAttr<MicroMipsAttr>()) |
76 | 0 | Fn->addFnAttr("micromips"); |
77 | 0 | else if (FD->hasAttr<NoMicroMipsAttr>()) |
78 | 0 | Fn->addFnAttr("nomicromips"); |
79 | |
|
80 | 0 | const MipsInterruptAttr *Attr = FD->getAttr<MipsInterruptAttr>(); |
81 | 0 | if (!Attr) |
82 | 0 | return; |
83 | | |
84 | 0 | const char *Kind; |
85 | 0 | switch (Attr->getInterrupt()) { |
86 | 0 | case MipsInterruptAttr::eic: Kind = "eic"; break; |
87 | 0 | case MipsInterruptAttr::sw0: Kind = "sw0"; break; |
88 | 0 | case MipsInterruptAttr::sw1: Kind = "sw1"; break; |
89 | 0 | case MipsInterruptAttr::hw0: Kind = "hw0"; break; |
90 | 0 | case MipsInterruptAttr::hw1: Kind = "hw1"; break; |
91 | 0 | case MipsInterruptAttr::hw2: Kind = "hw2"; break; |
92 | 0 | case MipsInterruptAttr::hw3: Kind = "hw3"; break; |
93 | 0 | case MipsInterruptAttr::hw4: Kind = "hw4"; break; |
94 | 0 | case MipsInterruptAttr::hw5: Kind = "hw5"; break; |
95 | 0 | } |
96 | | |
97 | 0 | Fn->addFnAttr("interrupt", Kind); |
98 | |
|
99 | 0 | } |
100 | | |
101 | | bool initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, |
102 | | llvm::Value *Address) const override; |
103 | | |
104 | 0 | unsigned getSizeOfUnwindException() const override { |
105 | 0 | return SizeOfUnwindException; |
106 | 0 | } |
107 | | }; |
108 | | } |
109 | | |
110 | | void MipsABIInfo::CoerceToIntArgs( |
111 | 0 | uint64_t TySize, SmallVectorImpl<llvm::Type *> &ArgList) const { |
112 | 0 | llvm::IntegerType *IntTy = |
113 | 0 | llvm::IntegerType::get(getVMContext(), MinABIStackAlignInBytes * 8); |
114 | | |
115 | | // Add (TySize / MinABIStackAlignInBytes) args of IntTy. |
116 | 0 | for (unsigned N = TySize / (MinABIStackAlignInBytes * 8); N; --N) |
117 | 0 | ArgList.push_back(IntTy); |
118 | | |
119 | | // If necessary, add one more integer type to ArgList. |
120 | 0 | unsigned R = TySize % (MinABIStackAlignInBytes * 8); |
121 | |
|
122 | 0 | if (R) |
123 | 0 | ArgList.push_back(llvm::IntegerType::get(getVMContext(), R)); |
124 | 0 | } |
125 | | |
126 | | // In N32/64, an aligned double precision floating point field is passed in |
127 | | // a register. |
128 | 0 | llvm::Type* MipsABIInfo::HandleAggregates(QualType Ty, uint64_t TySize) const { |
129 | 0 | SmallVector<llvm::Type*, 8> ArgList, IntArgList; |
130 | |
|
131 | 0 | if (IsO32) { |
132 | 0 | CoerceToIntArgs(TySize, ArgList); |
133 | 0 | return llvm::StructType::get(getVMContext(), ArgList); |
134 | 0 | } |
135 | | |
136 | 0 | if (Ty->isComplexType()) |
137 | 0 | return CGT.ConvertType(Ty); |
138 | | |
139 | 0 | const RecordType *RT = Ty->getAs<RecordType>(); |
140 | | |
141 | | // Unions/vectors are passed in integer registers. |
142 | 0 | if (!RT || !RT->isStructureOrClassType()) { |
143 | 0 | CoerceToIntArgs(TySize, ArgList); |
144 | 0 | return llvm::StructType::get(getVMContext(), ArgList); |
145 | 0 | } |
146 | | |
147 | 0 | const RecordDecl *RD = RT->getDecl(); |
148 | 0 | const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); |
149 | 0 | assert(!(TySize % 8) && "Size of structure must be multiple of 8."); |
150 | | |
151 | 0 | uint64_t LastOffset = 0; |
152 | 0 | unsigned idx = 0; |
153 | 0 | llvm::IntegerType *I64 = llvm::IntegerType::get(getVMContext(), 64); |
154 | | |
155 | | // Iterate over fields in the struct/class and check if there are any aligned |
156 | | // double fields. |
157 | 0 | for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); |
158 | 0 | i != e; ++i, ++idx) { |
159 | 0 | const QualType Ty = i->getType(); |
160 | 0 | const BuiltinType *BT = Ty->getAs<BuiltinType>(); |
161 | |
|
162 | 0 | if (!BT || BT->getKind() != BuiltinType::Double) |
163 | 0 | continue; |
164 | | |
165 | 0 | uint64_t Offset = Layout.getFieldOffset(idx); |
166 | 0 | if (Offset % 64) // Ignore doubles that are not aligned. |
167 | 0 | continue; |
168 | | |
169 | | // Add ((Offset - LastOffset) / 64) args of type i64. |
170 | 0 | for (unsigned j = (Offset - LastOffset) / 64; j > 0; --j) |
171 | 0 | ArgList.push_back(I64); |
172 | | |
173 | | // Add double type. |
174 | 0 | ArgList.push_back(llvm::Type::getDoubleTy(getVMContext())); |
175 | 0 | LastOffset = Offset + 64; |
176 | 0 | } |
177 | |
|
178 | 0 | CoerceToIntArgs(TySize - LastOffset, IntArgList); |
179 | 0 | ArgList.append(IntArgList.begin(), IntArgList.end()); |
180 | |
|
181 | 0 | return llvm::StructType::get(getVMContext(), ArgList); |
182 | 0 | } |
183 | | |
184 | | llvm::Type *MipsABIInfo::getPaddingType(uint64_t OrigOffset, |
185 | 0 | uint64_t Offset) const { |
186 | 0 | if (OrigOffset + MinABIStackAlignInBytes > Offset) |
187 | 0 | return nullptr; |
188 | | |
189 | 0 | return llvm::IntegerType::get(getVMContext(), (Offset - OrigOffset) * 8); |
190 | 0 | } |
191 | | |
192 | | ABIArgInfo |
193 | 0 | MipsABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const { |
194 | 0 | Ty = useFirstFieldIfTransparentUnion(Ty); |
195 | |
|
196 | 0 | uint64_t OrigOffset = Offset; |
197 | 0 | uint64_t TySize = getContext().getTypeSize(Ty); |
198 | 0 | uint64_t Align = getContext().getTypeAlign(Ty) / 8; |
199 | |
|
200 | 0 | Align = std::clamp(Align, (uint64_t)MinABIStackAlignInBytes, |
201 | 0 | (uint64_t)StackAlignInBytes); |
202 | 0 | unsigned CurrOffset = llvm::alignTo(Offset, Align); |
203 | 0 | Offset = CurrOffset + llvm::alignTo(TySize, Align * 8) / 8; |
204 | |
|
205 | 0 | if (isAggregateTypeForABI(Ty) || Ty->isVectorType()) { |
206 | | // Ignore empty aggregates. |
207 | 0 | if (TySize == 0) |
208 | 0 | return ABIArgInfo::getIgnore(); |
209 | | |
210 | 0 | if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) { |
211 | 0 | Offset = OrigOffset + MinABIStackAlignInBytes; |
212 | 0 | return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory); |
213 | 0 | } |
214 | | |
215 | | // If we have reached here, aggregates are passed directly by coercing to |
216 | | // another structure type. Padding is inserted if the offset of the |
217 | | // aggregate is unaligned. |
218 | 0 | ABIArgInfo ArgInfo = |
219 | 0 | ABIArgInfo::getDirect(HandleAggregates(Ty, TySize), 0, |
220 | 0 | getPaddingType(OrigOffset, CurrOffset)); |
221 | 0 | ArgInfo.setInReg(true); |
222 | 0 | return ArgInfo; |
223 | 0 | } |
224 | | |
225 | | // Treat an enum type as its underlying type. |
226 | 0 | if (const EnumType *EnumTy = Ty->getAs<EnumType>()) |
227 | 0 | Ty = EnumTy->getDecl()->getIntegerType(); |
228 | | |
229 | | // Make sure we pass indirectly things that are too large. |
230 | 0 | if (const auto *EIT = Ty->getAs<BitIntType>()) |
231 | 0 | if (EIT->getNumBits() > 128 || |
232 | 0 | (EIT->getNumBits() > 64 && |
233 | 0 | !getContext().getTargetInfo().hasInt128Type())) |
234 | 0 | return getNaturalAlignIndirect(Ty); |
235 | | |
236 | | // All integral types are promoted to the GPR width. |
237 | 0 | if (Ty->isIntegralOrEnumerationType()) |
238 | 0 | return extendType(Ty); |
239 | | |
240 | 0 | return ABIArgInfo::getDirect( |
241 | 0 | nullptr, 0, IsO32 ? nullptr : getPaddingType(OrigOffset, CurrOffset)); |
242 | 0 | } |
243 | | |
244 | | llvm::Type* |
245 | 0 | MipsABIInfo::returnAggregateInRegs(QualType RetTy, uint64_t Size) const { |
246 | 0 | const RecordType *RT = RetTy->getAs<RecordType>(); |
247 | 0 | SmallVector<llvm::Type*, 8> RTList; |
248 | |
|
249 | 0 | if (RT && RT->isStructureOrClassType()) { |
250 | 0 | const RecordDecl *RD = RT->getDecl(); |
251 | 0 | const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); |
252 | 0 | unsigned FieldCnt = Layout.getFieldCount(); |
253 | | |
254 | | // N32/64 returns struct/classes in floating point registers if the |
255 | | // following conditions are met: |
256 | | // 1. The size of the struct/class is no larger than 128-bit. |
257 | | // 2. The struct/class has one or two fields all of which are floating |
258 | | // point types. |
259 | | // 3. The offset of the first field is zero (this follows what gcc does). |
260 | | // |
261 | | // Any other composite results are returned in integer registers. |
262 | | // |
263 | 0 | if (FieldCnt && (FieldCnt <= 2) && !Layout.getFieldOffset(0)) { |
264 | 0 | RecordDecl::field_iterator b = RD->field_begin(), e = RD->field_end(); |
265 | 0 | for (; b != e; ++b) { |
266 | 0 | const BuiltinType *BT = b->getType()->getAs<BuiltinType>(); |
267 | |
|
268 | 0 | if (!BT || !BT->isFloatingPoint()) |
269 | 0 | break; |
270 | | |
271 | 0 | RTList.push_back(CGT.ConvertType(b->getType())); |
272 | 0 | } |
273 | |
|
274 | 0 | if (b == e) |
275 | 0 | return llvm::StructType::get(getVMContext(), RTList, |
276 | 0 | RD->hasAttr<PackedAttr>()); |
277 | | |
278 | 0 | RTList.clear(); |
279 | 0 | } |
280 | 0 | } |
281 | | |
282 | 0 | CoerceToIntArgs(Size, RTList); |
283 | 0 | return llvm::StructType::get(getVMContext(), RTList); |
284 | 0 | } |
285 | | |
286 | 0 | ABIArgInfo MipsABIInfo::classifyReturnType(QualType RetTy) const { |
287 | 0 | uint64_t Size = getContext().getTypeSize(RetTy); |
288 | |
|
289 | 0 | if (RetTy->isVoidType()) |
290 | 0 | return ABIArgInfo::getIgnore(); |
291 | | |
292 | | // O32 doesn't treat zero-sized structs differently from other structs. |
293 | | // However, N32/N64 ignores zero sized return values. |
294 | 0 | if (!IsO32 && Size == 0) |
295 | 0 | return ABIArgInfo::getIgnore(); |
296 | | |
297 | 0 | if (isAggregateTypeForABI(RetTy) || RetTy->isVectorType()) { |
298 | 0 | if (Size <= 128) { |
299 | 0 | if (RetTy->isAnyComplexType()) |
300 | 0 | return ABIArgInfo::getDirect(); |
301 | | |
302 | | // O32 returns integer vectors in registers and N32/N64 returns all small |
303 | | // aggregates in registers. |
304 | 0 | if (!IsO32 || |
305 | 0 | (RetTy->isVectorType() && !RetTy->hasFloatingRepresentation())) { |
306 | 0 | ABIArgInfo ArgInfo = |
307 | 0 | ABIArgInfo::getDirect(returnAggregateInRegs(RetTy, Size)); |
308 | 0 | ArgInfo.setInReg(true); |
309 | 0 | return ArgInfo; |
310 | 0 | } |
311 | 0 | } |
312 | | |
313 | 0 | return getNaturalAlignIndirect(RetTy); |
314 | 0 | } |
315 | | |
316 | | // Treat an enum type as its underlying type. |
317 | 0 | if (const EnumType *EnumTy = RetTy->getAs<EnumType>()) |
318 | 0 | RetTy = EnumTy->getDecl()->getIntegerType(); |
319 | | |
320 | | // Make sure we pass indirectly things that are too large. |
321 | 0 | if (const auto *EIT = RetTy->getAs<BitIntType>()) |
322 | 0 | if (EIT->getNumBits() > 128 || |
323 | 0 | (EIT->getNumBits() > 64 && |
324 | 0 | !getContext().getTargetInfo().hasInt128Type())) |
325 | 0 | return getNaturalAlignIndirect(RetTy); |
326 | | |
327 | 0 | if (isPromotableIntegerTypeForABI(RetTy)) |
328 | 0 | return ABIArgInfo::getExtend(RetTy); |
329 | | |
330 | 0 | if ((RetTy->isUnsignedIntegerOrEnumerationType() || |
331 | 0 | RetTy->isSignedIntegerOrEnumerationType()) && Size == 32 && !IsO32) |
332 | 0 | return ABIArgInfo::getSignExtend(RetTy); |
333 | | |
334 | 0 | return ABIArgInfo::getDirect(); |
335 | 0 | } |
336 | | |
337 | 0 | void MipsABIInfo::computeInfo(CGFunctionInfo &FI) const { |
338 | 0 | ABIArgInfo &RetInfo = FI.getReturnInfo(); |
339 | 0 | if (!getCXXABI().classifyReturnType(FI)) |
340 | 0 | RetInfo = classifyReturnType(FI.getReturnType()); |
341 | | |
342 | | // Check if a pointer to an aggregate is passed as a hidden argument. |
343 | 0 | uint64_t Offset = RetInfo.isIndirect() ? MinABIStackAlignInBytes : 0; |
344 | |
|
345 | 0 | for (auto &I : FI.arguments()) |
346 | 0 | I.info = classifyArgumentType(I.type, Offset); |
347 | 0 | } |
348 | | |
349 | | Address MipsABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, |
350 | 0 | QualType OrigTy) const { |
351 | 0 | QualType Ty = OrigTy; |
352 | | |
353 | | // Integer arguments are promoted to 32-bit on O32 and 64-bit on N32/N64. |
354 | | // Pointers are also promoted in the same way but this only matters for N32. |
355 | 0 | unsigned SlotSizeInBits = IsO32 ? 32 : 64; |
356 | 0 | unsigned PtrWidth = getTarget().getPointerWidth(LangAS::Default); |
357 | 0 | bool DidPromote = false; |
358 | 0 | if ((Ty->isIntegerType() && |
359 | 0 | getContext().getIntWidth(Ty) < SlotSizeInBits) || |
360 | 0 | (Ty->isPointerType() && PtrWidth < SlotSizeInBits)) { |
361 | 0 | DidPromote = true; |
362 | 0 | Ty = getContext().getIntTypeForBitwidth(SlotSizeInBits, |
363 | 0 | Ty->isSignedIntegerType()); |
364 | 0 | } |
365 | |
|
366 | 0 | auto TyInfo = getContext().getTypeInfoInChars(Ty); |
367 | | |
368 | | // The alignment of things in the argument area is never larger than |
369 | | // StackAlignInBytes. |
370 | 0 | TyInfo.Align = |
371 | 0 | std::min(TyInfo.Align, CharUnits::fromQuantity(StackAlignInBytes)); |
372 | | |
373 | | // MinABIStackAlignInBytes is the size of argument slots on the stack. |
374 | 0 | CharUnits ArgSlotSize = CharUnits::fromQuantity(MinABIStackAlignInBytes); |
375 | |
|
376 | 0 | Address Addr = emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*indirect*/ false, |
377 | 0 | TyInfo, ArgSlotSize, /*AllowHigherAlign*/ true); |
378 | | |
379 | | |
380 | | // If there was a promotion, "unpromote" into a temporary. |
381 | | // TODO: can we just use a pointer into a subset of the original slot? |
382 | 0 | if (DidPromote) { |
383 | 0 | Address Temp = CGF.CreateMemTemp(OrigTy, "vaarg.promotion-temp"); |
384 | 0 | llvm::Value *Promoted = CGF.Builder.CreateLoad(Addr); |
385 | | |
386 | | // Truncate down to the right width. |
387 | 0 | llvm::Type *IntTy = (OrigTy->isIntegerType() ? Temp.getElementType() |
388 | 0 | : CGF.IntPtrTy); |
389 | 0 | llvm::Value *V = CGF.Builder.CreateTrunc(Promoted, IntTy); |
390 | 0 | if (OrigTy->isPointerType()) |
391 | 0 | V = CGF.Builder.CreateIntToPtr(V, Temp.getElementType()); |
392 | |
|
393 | 0 | CGF.Builder.CreateStore(V, Temp); |
394 | 0 | Addr = Temp; |
395 | 0 | } |
396 | |
|
397 | 0 | return Addr; |
398 | 0 | } |
399 | | |
400 | 0 | ABIArgInfo MipsABIInfo::extendType(QualType Ty) const { |
401 | 0 | int TySize = getContext().getTypeSize(Ty); |
402 | | |
403 | | // MIPS64 ABI requires unsigned 32 bit integers to be sign extended. |
404 | 0 | if (Ty->isUnsignedIntegerOrEnumerationType() && TySize == 32) |
405 | 0 | return ABIArgInfo::getSignExtend(Ty); |
406 | | |
407 | 0 | return ABIArgInfo::getExtend(Ty); |
408 | 0 | } |
409 | | |
410 | | bool |
411 | | MIPSTargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, |
412 | 0 | llvm::Value *Address) const { |
413 | | // This information comes from gcc's implementation, which seems to |
414 | | // as canonical as it gets. |
415 | | |
416 | | // Everything on MIPS is 4 bytes. Double-precision FP registers |
417 | | // are aliased to pairs of single-precision FP registers. |
418 | 0 | llvm::Value *Four8 = llvm::ConstantInt::get(CGF.Int8Ty, 4); |
419 | | |
420 | | // 0-31 are the general purpose registers, $0 - $31. |
421 | | // 32-63 are the floating-point registers, $f0 - $f31. |
422 | | // 64 and 65 are the multiply/divide registers, $hi and $lo. |
423 | | // 66 is the (notional, I think) register for signal-handler return. |
424 | 0 | AssignToArrayRange(CGF.Builder, Address, Four8, 0, 65); |
425 | | |
426 | | // 67-74 are the floating-point status registers, $fcc0 - $fcc7. |
427 | | // They are one bit wide and ignored here. |
428 | | |
429 | | // 80-111 are the coprocessor 0 registers, $c0r0 - $c0r31. |
430 | | // (coprocessor 1 is the FP unit) |
431 | | // 112-143 are the coprocessor 2 registers, $c2r0 - $c2r31. |
432 | | // 144-175 are the coprocessor 3 registers, $c3r0 - $c3r31. |
433 | | // 176-181 are the DSP accumulator registers. |
434 | 0 | AssignToArrayRange(CGF.Builder, Address, Four8, 80, 181); |
435 | 0 | return false; |
436 | 0 | } |
437 | | |
438 | | std::unique_ptr<TargetCodeGenInfo> |
439 | 0 | CodeGen::createMIPSTargetCodeGenInfo(CodeGenModule &CGM, bool IsOS32) { |
440 | 0 | return std::make_unique<MIPSTargetCodeGenInfo>(CGM.getTypes(), IsOS32); |
441 | 0 | } |