1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "builtins.h"
17 #include "llvm_ark_interface.h"
18 #include "runtime_calls.h"
19 #include "utils.h"
20 #include "lowering/gc_barriers.h"
21 #include "lowering/metadata.h"
22
23 #include "compiler/optimizer/ir/basicblock.h"
24 #include "compiler/optimizer/ir/inst.h"
25
26 #include <llvm/IR/DebugInfoMetadata.h>
27 #include <llvm/IR/MDBuilder.h>
28 #include <llvm/ADT/SmallVector.h>
29 #include <llvm/Transforms/Utils/BasicBlockUtils.h>
30
31 using ark::llvmbackend::LLVMArkInterface;
32 using ark::llvmbackend::Metadata::BranchWeights::LIKELY_BRANCH_WEIGHT;
33 using ark::llvmbackend::Metadata::BranchWeights::UNLIKELY_BRANCH_WEIGHT;
34 using ark::llvmbackend::runtime_calls::GetThreadRegValue;
35 using ark::llvmbackend::utils::CopyDebugLoc;
36 using ark::llvmbackend::utils::CopyDeoptBundle;
37
38 namespace {
CreateEntrypointCallHelper(llvm::IRBuilder<> * builder,ark::compiler::RuntimeInterface::EntrypointId id,llvm::ArrayRef<llvm::Value * > arguments,LLVMArkInterface * arkInterface,llvm::CallInst * inst)39 llvm::CallInst *CreateEntrypointCallHelper(llvm::IRBuilder<> *builder, ark::compiler::RuntimeInterface::EntrypointId id,
40 llvm::ArrayRef<llvm::Value *> arguments, LLVMArkInterface *arkInterface,
41 llvm::CallInst *inst)
42 {
43 auto threadReg = GetThreadRegValue(builder, arkInterface);
44 auto call = ark::llvmbackend::runtime_calls::CreateEntrypointCallCommon(
45 builder, threadReg, arkInterface, static_cast<ark::llvmbackend::runtime_calls::EntrypointId>(id), arguments,
46 CopyDeoptBundle(inst));
47 CopyDebugLoc(inst, call);
48 if (inst->hasFnAttr("inline-info")) {
49 call->addFnAttr(inst->getFnAttr("inline-info"));
50 }
51
52 return call;
53 }
PostWRBHelper(llvm::IRBuilder<> * builder,llvm::CallInst * inst,LLVMArkInterface * arkInterface)54 llvm::Value *PostWRBHelper(llvm::IRBuilder<> *builder, llvm::CallInst *inst, LLVMArkInterface *arkInterface)
55 {
56 auto mem = inst->getOperand(0U);
57 auto offset = inst->getOperand(1U);
58 auto value = inst->getOperand(2U);
59
60 ASSERT(!arkInterface->IsIrtocMode());
61 auto threadReg = GetThreadRegValue(builder, arkInterface);
62 ark::llvmbackend::gc_barriers::EmitPostWRB(builder, mem, offset, value, arkInterface, threadReg, nullptr);
63 return nullptr;
64 }
FastClassLoadingHelper(llvm::IRBuilder<> * builder,LLVMArkInterface * arkInterface,llvm::BasicBlock * continuation)65 llvm::Value *FastClassLoadingHelper(llvm::IRBuilder<> *builder, LLVMArkInterface *arkInterface,
66 llvm::BasicBlock *continuation)
67 {
68 auto function = continuation->getParent();
69
70 llvm::Value *methodValue = function->arg_begin();
71
72 auto offset = arkInterface->GetClassOffset();
73 auto dataPtr = builder->CreateConstInBoundsGEP1_32(builder->getInt8Ty(), methodValue, offset);
74 auto classAddr = builder->CreateLoad(builder->getInt32Ty(), dataPtr);
75 auto result = builder->CreateIntToPtr(classAddr, builder->getPtrTy());
76 builder->CreateBr(continuation);
77 return result;
78 }
79
SlowClassLoadingHelper(llvm::IRBuilder<> * builder,llvm::CallInst * inst,LLVMArkInterface * arkInterface,llvm::BasicBlock * continuation,bool forceInit)80 llvm::Value *SlowClassLoadingHelper(llvm::IRBuilder<> *builder, llvm::CallInst *inst, LLVMArkInterface *arkInterface,
81 llvm::BasicBlock *continuation, bool forceInit)
82 {
83 using llvm::Value;
84 auto eid = forceInit ? ark::compiler::RuntimeInterface::EntrypointId::CLASS_INIT_RESOLVER
85 : ark::compiler::RuntimeInterface::EntrypointId::CLASS_RESOLVER;
86 auto initialBb = builder->GetInsertBlock();
87 auto function = initialBb->getParent();
88 auto module = function->getParent();
89 auto &ctx = builder->getContext();
90
91 // Helper functions
92 auto createUniqBasicBlockName = [&initialBb](const std::string &suffix) {
93 return ark::llvmbackend::LLVMArkInterface::GetUniqueBasicBlockName(initialBb->getName().str(), suffix);
94 };
95 auto createBasicBlock = [&ctx, &initialBb, &createUniqBasicBlockName](const std::string &suffix) {
96 auto name = createUniqBasicBlockName(suffix);
97 auto func = initialBb->getParent();
98 return llvm::BasicBlock::Create(ctx, name, func);
99 };
100
101 // Helper types & variables
102 auto aotGot = module->getGlobalVariable("__aot_got");
103 auto arrayType = llvm::ArrayType::get(builder->getInt64Ty(), 0);
104
105 // Input args
106 auto slotIdVal = inst->getOperand(1U);
107
108 /* Start generating code */
109
110 auto klassPtr = builder->CreateInBoundsGEP(arrayType, aotGot, {builder->getInt32(0), slotIdVal});
111 auto cachedKlassTmp = builder->CreateLoad(builder->getInt64Ty(), klassPtr, "cached_klass");
112 auto cachedKlass = builder->CreateIntToPtr(cachedKlassTmp, builder->getPtrTy());
113 auto resolutionRequired = builder->CreateIsNull(cachedKlass, "resolutionRequired");
114
115 auto weights = llvm::MDBuilder(ctx).createBranchWeights(UNLIKELY_BRANCH_WEIGHT, // if resolution_required
116 LIKELY_BRANCH_WEIGHT); // else
117 auto slowPath = createBasicBlock(std::string("slow_path"));
118 builder->CreateCondBr(resolutionRequired, slowPath, continuation, weights);
119
120 builder->SetInsertPoint(slowPath);
121
122 auto freshKlass = CreateEntrypointCallHelper(builder, eid, {klassPtr}, arkInterface, inst);
123 ASSERT(freshKlass->getCallingConv() == llvm::CallingConv::C);
124 freshKlass->setCallingConv(llvm::CallingConv::ArkResolver);
125 builder->CreateBr(continuation);
126
127 builder->SetInsertPoint(&continuation->front());
128 auto result = builder->CreatePHI(builder->getPtrTy(), 2U, "klass");
129 result->addIncoming(cachedKlass, initialBb);
130 result->addIncoming(freshKlass, slowPath);
131
132 return result;
133 }
134
LowerLoadClassHelper(llvm::IRBuilder<> * builder,llvm::CallInst * inst,LLVMArkInterface * arkInterface,bool forceInit)135 llvm::Value *LowerLoadClassHelper(llvm::IRBuilder<> *builder, llvm::CallInst *inst, LLVMArkInterface *arkInterface,
136 bool forceInit)
137 {
138 auto initialBb = builder->GetInsertBlock();
139 auto function = initialBb->getParent();
140
141 auto typeIdVal = inst->getOperand(0U);
142
143 llvm::BasicBlock *slowPath = initialBb;
144 llvm::BasicBlock *continuation = llvm::SplitBlock(initialBb, inst);
145
146 slowPath->back().eraseFromParent();
147 builder->SetInsertPoint(slowPath);
148
149 if (!llvm::isa<llvm::ConstantInt>(typeIdVal)) {
150 return SlowClassLoadingHelper(builder, inst, arkInterface, continuation, forceInit);
151 }
152
153 // Try simple constant case
154 auto functionMd = function->getMetadata(LLVMArkInterface::FUNCTION_MD_CLASS_ID);
155 ASSERT(functionMd != nullptr);
156 auto functionMdCasted = llvm::dyn_cast<llvm::ConstantAsMetadata>(functionMd->getOperand(0U))->getValue();
157 auto methodKlassId = llvm::cast<llvm::ConstantInt>(functionMdCasted)->getZExtValue();
158 auto requiredKlassId = llvm::cast<llvm::ConstantInt>(typeIdVal)->getZExtValue();
159
160 return methodKlassId == requiredKlassId
161 ? FastClassLoadingHelper(builder, arkInterface, continuation)
162 : SlowClassLoadingHelper(builder, inst, arkInterface, continuation, forceInit);
163 }
164
PreWRBHelper(llvm::IRBuilder<> * builder,llvm::CallInst * inst,LLVMArkInterface * arkInterface)165 llvm::Value *PreWRBHelper(llvm::IRBuilder<> *builder, llvm::CallInst *inst, LLVMArkInterface *arkInterface)
166 {
167 ASSERT(!arkInterface->IsIrtocMode());
168 auto initialBb = builder->GetInsertBlock();
169 auto mem = inst->getOperand(0U);
170 auto op1 = inst->getOperand(1U);
171 ASSERT(llvm::isa<llvm::ConstantInt>(op1));
172 auto isConst = llvm::cast<llvm::ConstantInt>(op1);
173 auto isVolatileMem = !isConst->isZero();
174
175 llvm::BasicBlock *continuation = llvm::SplitBlock(initialBb, inst);
176
177 initialBb->back().eraseFromParent();
178 builder->SetInsertPoint(initialBb);
179 auto threadRegValue = GetThreadRegValue(builder, arkInterface);
180 ark::llvmbackend::gc_barriers::EmitPreWRB(builder, mem, isVolatileMem, continuation, arkInterface, threadRegValue);
181 return nullptr;
182 }
183 } // namespace
184
185 namespace ark::llvmbackend::builtins {
186
LenArray(llvm::Module * module)187 llvm::Function *LenArray(llvm::Module *module)
188 {
189 auto function = module->getFunction(LEN_ARRAY_BUILTIN);
190 if (function != nullptr) {
191 return function;
192 }
193 auto type = llvm::FunctionType::get(llvm::Type::getInt32Ty(module->getContext()),
194 {llvm::PointerType::get(module->getContext(), LLVMArkInterface::GC_ADDR_SPACE),
195 llvm::Type::getInt32Ty(module->getContext())},
196 false);
197 function = llvm::Function::Create(type, llvm::Function::ExternalLinkage, LEN_ARRAY_BUILTIN, module);
198 function->setDoesNotThrow();
199 function->setSectionPrefix(BUILTIN_SECTION);
200 function->addFnAttr(llvm::Attribute::ReadNone);
201 function->addFnAttr(llvm::Attribute::WillReturn);
202 return function;
203 }
204
KeepThis(llvm::Module * module)205 llvm::Function *KeepThis(llvm::Module *module)
206 {
207 auto function = module->getFunction(KEEP_THIS_BUILTIN);
208 if (function != nullptr) {
209 return function;
210 }
211
212 auto type =
213 llvm::FunctionType::get(llvm::Type::getVoidTy(module->getContext()),
214 {llvm::PointerType::get(module->getContext(), LLVMArkInterface::GC_ADDR_SPACE)}, false);
215 function = llvm::Function::Create(type, llvm::Function::ExternalLinkage, KEEP_THIS_BUILTIN, module);
216 function->setDoesNotThrow();
217 function->setSectionPrefix(BUILTIN_SECTION);
218 function->addFnAttr(llvm::Attribute::WillReturn);
219 return function;
220 }
221
BarrierReturnVoid(llvm::Module * module)222 llvm::Function *BarrierReturnVoid(llvm::Module *module)
223 {
224 auto function = module->getFunction(BARRIER_RETURN_VOID_BUILTIN);
225 if (function != nullptr) {
226 return function;
227 }
228
229 auto type = llvm::FunctionType::get(llvm::Type::getVoidTy(module->getContext()), {}, false);
230 function = llvm::Function::Create(type, llvm::Function::ExternalLinkage, BARRIER_RETURN_VOID_BUILTIN, module);
231 function->setDoesNotThrow();
232 function->setSectionPrefix(BUILTIN_SECTION);
233 function->addFnAttr(llvm::Attribute::WillReturn);
234 return function;
235 }
236
LoadClass(llvm::Module * module)237 llvm::Function *LoadClass(llvm::Module *module)
238 {
239 auto function = module->getFunction(LOAD_CLASS_BUILTIN);
240 if (function != nullptr) {
241 return function;
242 }
243
244 auto type = llvm::FunctionType::get(
245 llvm::PointerType::get(module->getContext(), 0),
246 {llvm::Type::getInt32Ty(module->getContext()), llvm::Type::getInt32Ty(module->getContext())}, false);
247 function = llvm::Function::Create(type, llvm::Function::ExternalLinkage, LOAD_CLASS_BUILTIN, module);
248 function->setDoesNotThrow();
249 function->setSectionPrefix(BUILTIN_SECTION);
250 return function;
251 }
252
LoadInitClass(llvm::Module * module)253 llvm::Function *LoadInitClass(llvm::Module *module)
254 {
255 auto function = module->getFunction(LOAD_INIT_CLASS_BUILTIN);
256 if (function != nullptr) {
257 return function;
258 }
259
260 auto type = llvm::FunctionType::get(
261 llvm::PointerType::get(module->getContext(), 0),
262 {llvm::Type::getInt32Ty(module->getContext()), llvm::Type::getInt32Ty(module->getContext())}, false);
263 function = llvm::Function::Create(type, llvm::Function::ExternalLinkage, LOAD_INIT_CLASS_BUILTIN, module);
264 function->setDoesNotThrow();
265 function->setSectionPrefix(BUILTIN_SECTION);
266 return function;
267 }
268
PreWRB(llvm::Module * module,unsigned addrSpace)269 llvm::Function *PreWRB(llvm::Module *module, unsigned addrSpace)
270 {
271 auto builtinName = (addrSpace == LLVMArkInterface::GC_ADDR_SPACE) ? PRE_WRB_GCADR_BUILTIN : PRE_WRB_BUILTIN;
272 auto function = module->getFunction(builtinName);
273 if (function != nullptr) {
274 return function;
275 }
276 auto &ctx = module->getContext();
277 auto type = llvm::FunctionType::get(llvm::Type::getVoidTy(ctx),
278 {llvm::PointerType::get(ctx, addrSpace), llvm::Type::getInt1Ty(ctx)}, false);
279 function = llvm::Function::Create(type, llvm::Function::ExternalLinkage, builtinName, module);
280 function->setDoesNotThrow();
281 function->setSectionPrefix(BUILTIN_SECTION);
282 function->addFnAttr(llvm::Attribute::ArgMemOnly);
283 function->addFnAttr(llvm::Attribute::WillReturn);
284 function->addParamAttr(0U, llvm::Attribute::ReadOnly);
285 return function;
286 }
287
PostWRB(llvm::Module * module,unsigned addrSpace)288 llvm::Function *PostWRB(llvm::Module *module, unsigned addrSpace)
289 {
290 auto builtinName = (addrSpace == LLVMArkInterface::GC_ADDR_SPACE) ? POST_WRB_GCADR_BUILTIN : POST_WRB_BUILTIN;
291 auto function = module->getFunction(builtinName);
292 if (function != nullptr) {
293 return function;
294 }
295 auto &ctx = module->getContext();
296 auto type = llvm::FunctionType::get(llvm::Type::getVoidTy(ctx),
297 {llvm::PointerType::get(ctx, addrSpace), llvm::Type::getInt32Ty(ctx),
298 llvm::PointerType::get(ctx, LLVMArkInterface::GC_ADDR_SPACE)},
299 false);
300 function = llvm::Function::Create(type, llvm::Function::ExternalLinkage, builtinName, module);
301 function->setDoesNotThrow();
302 function->setSectionPrefix(BUILTIN_SECTION);
303 function->addFnAttr(llvm::Attribute::ArgMemOnly);
304 function->addFnAttr(llvm::Attribute::WillReturn);
305 function->addParamAttr(0U, llvm::Attribute::ReadNone);
306 function->addParamAttr(2U, llvm::Attribute::ReadNone);
307 return function;
308 }
309
LoadString(llvm::Module * module)310 llvm::Function *LoadString(llvm::Module *module)
311 {
312 auto function = module->getFunction(LOAD_STRING_BUILTIN);
313 if (function != nullptr) {
314 return function;
315 }
316
317 auto type = llvm::FunctionType::get(
318 llvm::PointerType::get(module->getContext(), LLVMArkInterface::GC_ADDR_SPACE),
319 {llvm::Type::getInt32Ty(module->getContext()), llvm::Type::getInt32Ty(module->getContext())}, false);
320 function = llvm::Function::Create(type, llvm::Function::ExternalLinkage, LOAD_STRING_BUILTIN, module);
321 function->setDoesNotThrow();
322 function->setSectionPrefix(BUILTIN_SECTION);
323 return function;
324 }
325
ResolveVirtual(llvm::Module * module)326 llvm::Function *ResolveVirtual(llvm::Module *module)
327 {
328 auto function = module->getFunction(RESOLVE_VIRTUAL_BUILTIN);
329 if (function != nullptr) {
330 return function;
331 }
332 auto type = llvm::FunctionType::get(llvm::PointerType::get(module->getContext(), 0),
333 {llvm::PointerType::get(module->getContext(), LLVMArkInterface::GC_ADDR_SPACE),
334 llvm::Type::getInt64Ty(module->getContext()),
335 llvm::PointerType::get(module->getContext(), 0)},
336 false);
337 function = llvm::Function::Create(type, llvm::Function::ExternalLinkage, RESOLVE_VIRTUAL_BUILTIN, module);
338 function->setDoesNotThrow();
339 function->setSectionPrefix(BUILTIN_SECTION);
340 return function;
341 }
342
LowerLenArray(llvm::IRBuilder<> * builder,llvm::CallInst * inst)343 llvm::Value *LowerLenArray(llvm::IRBuilder<> *builder, llvm::CallInst *inst)
344 {
345 auto array = inst->getOperand(0U);
346 auto offset = inst->getOperand(1U);
347
348 auto rawPtr = builder->CreateInBoundsGEP(builder->getInt8Ty(), array, offset);
349
350 return builder->CreateLoad(builder->getInt32Ty(), rawPtr);
351 }
352
LowerLoadString(llvm::IRBuilder<> * builder,llvm::CallInst * inst,LLVMArkInterface * arkInterface)353 llvm::Value *LowerLoadString(llvm::IRBuilder<> *builder, llvm::CallInst *inst, LLVMArkInterface *arkInterface)
354 {
355 auto module = inst->getModule();
356 auto function = inst->getFunction();
357 auto aotGot = module->getGlobalVariable("__aot_got");
358 auto arrayType = llvm::ArrayType::get(builder->getInt64Ty(), 0);
359
360 // Input args
361 auto typeId = inst->getOperand(0U);
362 auto slotId = inst->getOperand(1U);
363
364 auto slotPtr = builder->CreateInBoundsGEP(arrayType, aotGot, {builder->getInt32(0), slotId});
365 auto str = builder->CreateLoad(builder->getInt64Ty(), slotPtr);
366 auto limit = compiler::RuntimeInterface::RESOLVE_STRING_AOT_COUNTER_LIMIT;
367 auto cmp = builder->CreateICmpULT(str, builder->getInt64(limit));
368
369 llvm::Instruction *ifi;
370 llvm::Instruction *elsi;
371 auto weights =
372 llvm::MDBuilder(inst->getContext()).createBranchWeights(UNLIKELY_BRANCH_WEIGHT, LIKELY_BRANCH_WEIGHT);
373 SplitBlockAndInsertIfThenElse(cmp, inst, &ifi, &elsi, weights);
374
375 ifi->getParent()->setName("load_str_slow");
376 elsi->getParent()->setName("load_str_fast");
377
378 builder->SetInsertPoint(ifi);
379 auto method = function->arg_begin();
380 auto eid = compiler::RuntimeInterface::EntrypointId::RESOLVE_STRING_AOT;
381 auto freshStr = CreateEntrypointCallHelper(builder, eid, {method, typeId, slotPtr}, arkInterface, inst);
382 freshStr->setName("fresh_str");
383
384 builder->SetInsertPoint(elsi);
385 auto cachedStr = builder->CreateIntToPtr(str, inst->getType());
386 cachedStr->setName("cached_str");
387
388 builder->SetInsertPoint(inst);
389 auto resStr = builder->CreatePHI(inst->getType(), 2U);
390 resStr->addIncoming(freshStr, ifi->getParent());
391 resStr->addIncoming(cachedStr, elsi->getParent());
392 return resStr;
393 }
394
LowerResolveVirtual(llvm::IRBuilder<> * builder,llvm::CallInst * inst,LLVMArkInterface * arkInterface)395 llvm::Value *LowerResolveVirtual(llvm::IRBuilder<> *builder, llvm::CallInst *inst, LLVMArkInterface *arkInterface)
396 {
397 llvm::CallInst *entrypointAddress;
398 auto method = inst->getFunction()->arg_begin();
399 auto thiz = inst->getOperand(0U);
400 auto methodId = inst->getOperand(1U);
401 ASSERT(!llvm::isa<llvm::PoisonValue>(thiz));
402 if (!arkInterface->IsArm64() || !llvm::isa<llvm::ConstantInt>(methodId)) {
403 static constexpr auto ENTRYPOINT_ID = compiler::RuntimeInterface::EntrypointId::RESOLVE_VIRTUAL_CALL_AOT;
404 auto offset = builder->getInt64(0);
405 entrypointAddress =
406 CreateEntrypointCallHelper(builder, ENTRYPOINT_ID, {method, thiz, methodId, offset}, arkInterface, inst);
407 } else {
408 auto offset = inst->getOperand(2U);
409 static constexpr auto ENTRYPOINT_ID = compiler::RuntimeInterface::EntrypointId::INTF_INLINE_CACHE;
410 entrypointAddress =
411 CreateEntrypointCallHelper(builder, ENTRYPOINT_ID, {method, thiz, methodId, offset}, arkInterface, inst);
412 ASSERT(entrypointAddress->getCallingConv() == llvm::CallingConv::C);
413 entrypointAddress->setCallingConv(llvm::CallingConv::ArkFast4);
414 }
415
416 return builder->CreateIntToPtr(entrypointAddress, builder->getPtrTy());
417 }
418
LowerBuiltin(llvm::IRBuilder<> * builder,llvm::CallInst * inst,LLVMArkInterface * arkInterface)419 llvm::Value *LowerBuiltin(llvm::IRBuilder<> *builder, llvm::CallInst *inst, LLVMArkInterface *arkInterface)
420 {
421 auto function = inst->getCalledFunction();
422 const auto &funcName = function->getName();
423 if (funcName.equals(LEN_ARRAY_BUILTIN)) {
424 return LowerLenArray(builder, inst);
425 }
426 if (funcName.equals(LOAD_CLASS_BUILTIN)) {
427 return LowerLoadClassHelper(builder, inst, arkInterface, false);
428 }
429 if (funcName.equals(LOAD_INIT_CLASS_BUILTIN)) {
430 return LowerLoadClassHelper(builder, inst, arkInterface, true);
431 }
432 if (funcName.equals(PRE_WRB_GCADR_BUILTIN) || funcName.equals(PRE_WRB_BUILTIN)) {
433 return PreWRBHelper(builder, inst, arkInterface);
434 }
435 if (funcName.equals(POST_WRB_GCADR_BUILTIN) || funcName.equals(POST_WRB_BUILTIN)) {
436 return PostWRBHelper(builder, inst, arkInterface);
437 }
438 if (funcName.equals(LOAD_STRING_BUILTIN)) {
439 return LowerLoadString(builder, inst, arkInterface);
440 }
441 if (funcName.equals(RESOLVE_VIRTUAL_BUILTIN)) {
442 return LowerResolveVirtual(builder, inst, arkInterface);
443 }
444 if (funcName.equals(BARRIER_RETURN_VOID_BUILTIN)) {
445 return nullptr;
446 }
447 if (funcName.equals(KEEP_THIS_BUILTIN)) {
448 return nullptr;
449 }
450 UNREACHABLE();
451 }
452 } // namespace ark::llvmbackend::builtins
453