1 /* 2 * Copyright (c) 2023 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 #ifndef MAPLE_LITECG_LMIR_BUILDER_H 17 #define MAPLE_LITECG_LMIR_BUILDER_H 18 19 #include <cstdint> 20 #include <variant> 21 #include <vector> 22 #include <map> 23 #include <string> 24 #include <utility> 25 #include <unordered_map> 26 #include "mpl_logging.h" 27 28 /* 29 LMIR API exported. 30 31 LMIR, the low-level MIR, will serve as the canonical input for LiteCG. 32 33 Currently, it only contains the minimum set of features enough to 34 support eCompiler scenarioes. Later it will grow to be a complete core 35 set, but still with "just enough" features for general cases. 36 37 Additional features, will be defined with x-extension strategy. 38 39 The motivation of LMIR, is to hide the whole complexity of MIR interfaces, 40 and this should make integration of maple components easier. 41 */ 42 namespace maple { 43 44 /* import types for MIR: this is basically a simplification 45 we just need declaration, not headers. 46 */ 47 class MIRBuilder; // currently we just delegate MIRBuilder 48 class MIRModule; 49 class MIRFunction; 50 class MIRType; 51 class MIRFuncType; 52 class MIRConst; 53 class MIRAggConst; 54 class MIRSymbol; 55 class StmtNode; 56 class BaseNode; 57 class BlockNode; 58 class MIRPreg; 59 60 namespace litecg { 61 62 // Our type abstraction. currently delegate to MIR 63 64 using String = std::string; 65 66 using Module = MIRModule; 67 using Function = MIRFunction; 68 69 // Note: Type is base class of all other Types 70 using Type = MIRType; // base class of all Types 71 using Const = MIRConst; 72 using StructConst = MIRAggConst; 73 using ArrayConst = MIRAggConst; 74 using Var = MIRSymbol; 75 using Stmt = StmtNode; 76 using BB = BlockNode; // A temporary faked BB 77 78 using Param = std::pair<const String, Type *>; 79 using Params = std::vector<Param>; 80 using FieldOffset = std::pair<int32_t, int32_t>; // (byteoffset, bitoffset) 81 using PregIdx = int32_t; 82 83 // enumerations 84 enum class MIRIntrinsic { 85 #define DEF_MIR_INTRINSIC(STR, NAME, INTRN_CLASS, RETURN_TYPE, ...) INTRN_##STR, 86 #include "intrinsics.def" 87 #undef DEF_MIR_INTRINSIC 88 }; 89 using IntrinsicId = MIRIntrinsic; 90 /* available intrinsic should be list here. like: 91 INTRN_memcpy, INTRN_memset, etc. 92 */ 93 class Expr { 94 public: Expr(BaseNode * baseNode,Type * nodeType)95 Expr(BaseNode *baseNode, Type *nodeType) : node(baseNode), type(nodeType) {} 96 Expr()97 Expr() : node(nullptr), type(nullptr) {} 98 99 ~Expr() = default; 100 GetNode()101 BaseNode *GetNode() 102 { 103 return node; 104 } 105 GetNode()106 BaseNode *GetNode() const 107 { 108 return node; 109 } 110 GetType()111 Type *GetType() 112 { 113 return type; 114 } 115 GetType()116 Type *GetType() const 117 { 118 return type; 119 } 120 121 bool IsDread() const; 122 bool IsRegread() const; 123 bool IsConstValue() const; 124 125 private: 126 BaseNode *node; 127 Type *type; 128 }; 129 130 enum LiteCGValueKind { 131 kPregKind, 132 kSymbolKind, 133 kConstKind, 134 kGlueAdd, 135 kPregPairKind, 136 }; 137 138 struct LiteCGValue { 139 LiteCGValueKind kind; 140 std::variant<PregIdx, MIRSymbol*, MIRConst*, std::pair<PregIdx, PregIdx>> data; 141 }; 142 143 using Args = std::vector<Expr>; 144 145 enum FuncAttr { // visibility of the defined function 146 FUNC_global, // function has global visibility 147 FUNC_weak, // weak function defined in this module 148 FUNC_internal, // function defined and only used in this module 149 }; 150 151 enum GlobalRegister { 152 kSregSp = -1, 153 kSregFp = -2, 154 }; 155 156 enum ConvAttr { 157 CCall, 158 Web_Kit_JS_Call, 159 GHC_Call, 160 }; 161 162 enum GlobalVarAttr { 163 VAR_external, // global variable declaration (no definition) 164 VAR_weak, // weak function defined in this module 165 VAR_internal, // variable defined and only used in this module 166 VAR_global, // exported variable defined in this module 167 VAR_readonly, // this is additional flag, default is R/W 168 }; 169 170 enum IntCmpCondition { 171 kEQ, 172 kNE, 173 kULT, 174 kULE, 175 kUGT, 176 kUGE, 177 kSLT, 178 kSLE, 179 kSGT, 180 kSGE, 181 }; 182 183 enum FloatCmpCondition { 184 kOLT, 185 kOLE, 186 kOGT, 187 kOGE, 188 kONE, 189 kOEQ, 190 }; 191 192 enum LiteCGTypeKind { 193 kLiteCGTypeUnknown, 194 kLiteCGTypeScalar, 195 kLiteCGTypeArray, 196 kLiteCGTypePointer, 197 kLiteCGTypeFunction, 198 kLiteCGTypeVoid, 199 kLiteCGTypeByName, 200 }; 201 202 // FieldAttr: do we need to support volatile here? 203 // using FieldAttr = AttrKind 204 /* used attribute for field: 205 ATTR_volatile 206 */ 207 208 using FieldId = uint32_t; 209 /* Use FieldId from MIR directly: it's a uint32_t, but with special meaning 210 for FieldId == 0: refer to the "whole" of the type 211 To avoid conflict with MIR type define, here using type name "FieldId" 212 */ 213 214 Module *CreateModuleWithName(const std::string &name); 215 void ReleaseModule(Module *module); 216 217 /** 218 * a simplified, specialized MapleIR builder for LiteCG 219 * The basic IR set 220 * 221 * General rule for the interface: 222 * + if function returns value type, then returns type is value type 223 * + otherwise if the return value can be null, return type is pointer 224 * caller should check for null 225 * + otherwise the return type is reference. 226 * 227 * + for compound IR (need to be constructed with a sequence of calls, 228 * e.g., struct type/const, array const, function, switch), using 229 * specific builder class to do the "chained" construction. 230 * 231 */ 232 class LMIRBuilder { 233 public: 234 LMIRBuilder(Module &module); 235 ~LMIRBuilder() = default; 236 237 #ifdef ARK_LITECG_DEBUG 238 void DumpIRToFile(const std::string fileName); 239 #endif 240 241 LiteCGTypeKind LiteCGGetTypeKind(Type *type) const; 242 void SetCallStmtDeoptBundleInfo(Stmt &icallNode, const std::unordered_map<int, LiteCGValue> &deoptBundleInfo); 243 244 // Type creation (currently all in global scope) 245 /* 246 For primitive types, using LMIRBuilder's public member: 247 i8Type, u8Type, etc. 248 */ 249 250 // derived type creation 251 Type *CreatePtrType(Type *mirType); 252 Type *CreateRefType(Type *mirType); 253 254 bool IsHeapPointerType(Type *mirType) const; 255 256 // for function pointer 257 Type *CreateFuncType(std::vector<Type *> params, Type *retType, bool isVarg); 258 259 Type *LiteCGGetPointedType(Type *type); 260 261 std::vector<Type *> LiteCGGetFuncParamTypes(Type *type); 262 263 Type *LiteCGGetFuncReturnType(Type *type); 264 // still need interface for AddressOfFunction 265 266 // Function declaration and definition 267 /* using FunctionBuilder interface for Function creation: 268 // i32 myfunc(i32 param1, i64 param2) { } 269 auto structType = DefineFunction("myfunc") 270 .Param(i32Type, "param1") 271 .Param(i64Type, "param2") 272 .Ret(i32Type) // optional for void 273 .Done(); 274 275 auto structType = DeclareFunction("myfunc1") 276 .Param(i32Type, "param1") 277 .Param(i64Type, "param2") 278 .Ret(i32Type) 279 .Done(); 280 */ 281 282 // This is to enable forwarded call before its definition 283 // can return null. caller should check for null. 284 Function *GetFunc(const String &name); // get a function by its unique name 285 286 // when a function is set as current function (of the module), local 287 // declarations and statements are insert into it. 288 void SetCurFunc(Function &function); 289 290 Function &GetCurFunction() const; 291 292 void RenameFormal2Preg(Function &func); 293 294 Expr LiteCGGetPregFP(Function &func); 295 Expr LiteCGGetPregSP(); 296 297 Var &CreateLocalVar(Type *type, const String &name); 298 Var *GetLocalVar(const String &name); 299 Var *GetLocalVarFromExpr(Expr inExpr); 300 void SetFunctionDerived2BaseRef(PregIdx derived, PregIdx base); 301 PregIdx GetPregIdxFromExpr(const Expr &expr); 302 Var &GetParam(Function &function, size_t index) const; 303 Expr GenExprFromVar(Var &var); 304 305 Const &CreateIntConst(Type *type, int64_t val); 306 Const &CreateDoubleConst(double val); 307 Const *GetConstFromExpr(const Expr &expr); 308 309 /* using ArrayConstBuilder interface for ArrayConst creation: 310 Note: the elements should be added consequentially, and match the dim size. 311 auto arrayConst = CreateArrayConst(arrayType) 312 .Element(CreateIntConst(i32Type, 0)) 313 .Element(CreateIntConst(i32Type, 0)) 314 .Done(); 315 or using the following form: 316 auto arrayConst = CreateArrayConst(arrayType).Dim({0, 0}).Done(); 317 */ 318 319 /* 320 BB is the container node for a sequence or linear statements, 321 if needLabel == true, implicitly create a label node as its first statement. 322 BB also servers as target for Gotos, when it's a goto target, it 323 should have needLabel == true 324 */ 325 BB &CreateBB(bool needLabel = true); 326 void AppendStmt(BB &bb, Stmt &stmt); // append stmt to the back of BB 327 void AppendStmtBeforeBranch(BB &bb, Stmt &stmt); // append stmt after the first non-jump stmt in back of BB 328 void AppendBB(BB &bb); // append BB to the back of current function; 329 void AppendToLast(BB &bb); 330 BB &GetLastPosBB(); 331 BB &GetLastAppendedBB(); // get last appended BB of current function 332 333 void SetStmtCallConv(Stmt &stmt, ConvAttr convAttr); 334 335 // statements 336 Stmt &Goto(BB &dest); // jump without condition 337 /* conditional goto: 338 when inverseCond == true, using (!cond) as condition 339 340 1. if(cond)-then form code should be generated this way: 341 342 if(!cond) goto BB_end // CondGoto(cond, BB_end, true); 343 BB_ifTrue: {...} 344 BB_end: {...} 345 346 2. if-then-else form code should be generated this way: 347 if(cond) goto BB_ifTrue // CondGoto(cond, BB_ifTrue); 348 BB_ifFalse: { 349 ... 350 goto BB_end // should be generated in BB_ifFalse 351 } 352 BB_ifTrue: {...} 353 BB_end: {...} 354 */ 355 Stmt &CondGoto(Expr cond, BB &target, bool inverseCond = false); 356 357 /* using SwitchBuilder interface for switch statement creation 358 auto switchStmt = Switch(type, cond, defaultBB) 359 .Case(0, bb1) 360 .Case(1, bb2) 361 .Done(); 362 */ 363 364 // when result is nullptr, don't need the result (or no result) 365 Stmt &Call(Function &func, Args &args, PregIdx pregIdx); 366 367 Stmt &PureCall(Expr funcAddr, Args &args, Var *result = nullptr); 368 369 Stmt &ICall(Expr funcAddr, Args &args, Var *result = nullptr); 370 371 Stmt &ICall(Expr funcAddr, Args &args, PregIdx pregIdx); 372 373 // when result is nullptr, don't need the result (or no result) 374 Stmt &IntrinsicCall(IntrinsicId func, Args &valueArgs, Var *result = nullptr); 375 376 Stmt &IntrinsicCall(IntrinsicId func, Args &valueArgs, PregIdx retPregIdx1, PregIdx retPregIdx2); 377 378 Stmt &Return(Expr returnVal); 379 380 // debug info 381 Stmt &Comment(std::string comment); 382 383 Stmt &Dassign(Expr src, Var &var, FieldId fieldId = 0); 384 Stmt &Iassign(Expr src, Expr addr, Type *baseType, FieldId fieldId = 0); 385 386 // expressions 387 Expr Dread(Var &var); // do we need other forms? Dread(Var * var)388 inline Expr Dread(Var *var) 389 { // shortcut for read from local-var 390 CHECK_NULL_FATAL(var); 391 return Dread(*var); 392 } 393 394 Expr IntrinsicOp(IntrinsicId id, Type *type, Args &args_); 395 Expr Iread(Type *type, Expr addr, Type *baseType, FieldId fieldId = 0); 396 PregIdx CreatePreg(Type *mtype); 397 Stmt &Regassign(Expr src, PregIdx reg); 398 Expr Regread(PregIdx pregIdx); 399 Expr ConstVal(Const &constVal); // a const operand 400 401 Expr Lnot(Type *type, Expr src); 402 Expr Bnot(Type *type, Expr src); 403 Expr Sqrt(Type *type, Expr src); 404 Expr Ceil(Type *type, Expr src); 405 Expr Abs(Type *type, Expr src); 406 407 Expr Add(Type *type, Expr src1, Expr src2); 408 Expr Sub(Type *type, Expr src1, Expr src2); 409 Expr Mul(Type *type, Expr src1, Expr src2); 410 Expr UDiv(Type *type, Expr src1, Expr src2); // unsigned 411 Expr SDiv(Type *type, Expr src1, Expr src2); // signed 412 Expr SRem(Type *type, Expr src1, Expr src2); // signed 413 Expr Shl(Type *type, Expr src1, Expr src2); 414 Expr LShr(Type *type, Expr src1, Expr src2); 415 Expr AShr(Type *type, Expr src1, Expr src2); 416 Expr And(Type *type, Expr src1, Expr src2); 417 Expr Or(Type *type, Expr src1, Expr src2); 418 Expr Xor(Type *type, Expr src1, Expr src2); 419 Expr Max(Type *type, Expr src1, Expr src2); 420 Expr Min(Type *type, Expr src1, Expr src2); 421 422 Expr ICmp(Type *type, Expr src1, Expr src2, IntCmpCondition cond); 423 Expr FCmp(Type *type, Expr src1, Expr src2, FloatCmpCondition cond); 424 425 // Type conversion 426 // Type of opnd should be consistent with fromType: no implicient conversion 427 Expr Trunc(Type *fromType, Type *toType, Expr opnd); 428 Expr ZExt(Type *fromType, Type *toType, Expr opnd); 429 Expr SExt(Type *fromType, Type *toType, Expr opnd); 430 Expr BitCast(Type *fromType, Type *toType, Expr opnd); 431 Expr Cvt(Type *fromType, Type *toType, Expr opnd); 432 Expr Floor(Type *fromType, Type *toType, Expr opnd); 433 Expr Ceil(Type *fromType, Type *toType, Expr opnd); 434 435 void SetFuncFrameResverdSlot(int slot); 436 void SetFuncFramePointer(const String &val); 437 void SetCurrentDebugComment(const std::string& comment); 438 void ClearCurrentDebugComment(); 439 440 public: 441 // helper classes for compound IR entity building 442 class SwitchBuilder { 443 public: SwitchBuilder(LMIRBuilder & builder_,Type * type_,Expr cond_,BB & defaultBB_)444 SwitchBuilder(LMIRBuilder &builder_, Type *type_, Expr cond_, BB &defaultBB_) 445 : builder(builder_), type(type_), cond(cond_), defaultBB(defaultBB_) 446 { 447 } 448 Case(int64_t value,BB & bb)449 SwitchBuilder &Case(int64_t value, BB &bb) 450 { 451 cases.push_back(std::make_pair(value, &bb)); 452 return *this; 453 } 454 Done()455 Stmt &Done() 456 { 457 return builder.CreateSwitchInternal(type, cond, defaultBB, cases); 458 } 459 460 private: 461 LMIRBuilder &builder; 462 Type *type; 463 Expr cond; 464 BB &defaultBB; 465 std::vector<std::pair<int64_t, BB *>> cases; 466 }; 467 Switch(Type * type,Expr cond,BB & defaultBB)468 SwitchBuilder Switch(Type *type, Expr cond, BB &defaultBB) 469 { 470 return SwitchBuilder(*this, type, cond, defaultBB); 471 } 472 473 class FunctionBuilder { 474 public: FunctionBuilder(LMIRBuilder & builder_,const String & name_,bool needBody_)475 FunctionBuilder(LMIRBuilder &builder_, const String &name_, bool needBody_) 476 : builder(builder_), name(name_), needBody(needBody_) 477 { 478 attr = FUNC_internal; 479 convAttr = CCall; 480 isVargs = false; 481 } 482 483 // optional: indicate the function has variable args Vargs()484 FunctionBuilder &Vargs() 485 { 486 isVargs = true; 487 return *this; 488 } 489 Param(Type * type,const String paramName)490 FunctionBuilder &Param(Type *type, const String paramName) 491 { 492 params.push_back(std::make_pair(paramName, type)); 493 return *this; 494 } 495 496 // optional: if not called, return nothing (return void) Return(Type * type)497 FunctionBuilder &Return(Type *type) 498 { 499 retType = type; 500 return *this; 501 } 502 503 // optional: if not called, default to FUNC_local Attribute(FuncAttr attr_)504 FunctionBuilder &Attribute(FuncAttr attr_) 505 { 506 attr = attr_; 507 return *this; 508 } 509 510 // optional: if not called, default to Func_CCall CallConvAttribute(ConvAttr convAttr_)511 FunctionBuilder &CallConvAttribute(ConvAttr convAttr_) 512 { 513 convAttr = convAttr_; 514 return *this; 515 } 516 Done()517 Function &Done() 518 { 519 return builder.CreateFunctionInternal(name, retType, params, isVargs, needBody, attr, convAttr); 520 } 521 522 private: 523 LMIRBuilder &builder; 524 const String &name; 525 Type *retType; 526 FuncAttr attr; 527 ConvAttr convAttr; 528 bool isVargs; 529 bool needBody; // indicate whether is a declaration or definition. 530 Params params; 531 }; 532 533 // only declare the function in current module (means it's an external function) DeclareFunction(const String & name)534 FunctionBuilder DeclareFunction(const String &name) 535 { 536 return FunctionBuilder(*this, name, false); 537 } 538 539 // define the function in current module DefineFunction(const String & name)540 FunctionBuilder DefineFunction(const String &name) 541 { 542 return FunctionBuilder(*this, name, true); 543 } 544 545 public: 546 // builtin types: primitive types (all MIR primitive types except PTY_ptr) 547 Type *i8Type; 548 Type *i16Type; 549 Type *i32Type; 550 Type *i64Type; 551 Type *u1Type; 552 Type *u8Type; 553 Type *u16Type; 554 Type *u32Type; 555 Type *u64Type; 556 Type *voidType; 557 Type *f32Type; 558 Type *f64Type; 559 560 // builtin types: commonly used derived types 561 Type *strType; 562 Type *i64PtrType; 563 Type *i64RefType; 564 Type *i64RefRefType; 565 566 private: 567 Stmt &CreateSwitchInternal(Type *type, Expr cond, BB &defaultBB, std::vector<std::pair<int64_t, BB *>> &cases); 568 void AddConstItemInternal(StructConst &structConst, FieldId fieldId, Const &field); 569 void AddConstItemInternal(ArrayConst &structConst, Const &element); 570 Function &CreateFunctionInternal(const String &name, Type *retType, Params ¶ms, bool isVargs, bool needBody, 571 FuncAttr attr, ConvAttr convAttr); 572 573 private: 574 MIRBuilder &mirBuilder; // The real IR-builder: current implementation 575 Module &module; // and the module to process 576 }; 577 578 } // namespace litecg 579 } // namespace maple 580 #endif // MAPLE_LITECG_LMIR_BUILDER_H 581