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 &DeoptCall(Function &func, Args &args); 366 367 Stmt &TailICall(Expr funcAddr, Args &args); 368 369 Stmt &PureCall(Expr funcAddr, Args &args, Var *result = nullptr); 370 371 Stmt &ICall(Expr funcAddr, Args &args, Var *result = nullptr); 372 373 Stmt &ICall(Expr funcAddr, Args &args, PregIdx pregIdx); 374 375 // when result is nullptr, don't need the result (or no result) 376 Stmt &IntrinsicCall(IntrinsicId func, Args &valueArgs, Var *result = nullptr); 377 378 Stmt &IntrinsicCall(IntrinsicId func, Args &valueArgs, PregIdx retPregIdx1, PregIdx retPregIdx2); 379 380 Stmt &Return(Expr returnVal); 381 382 // debug info 383 Stmt &Comment(std::string comment); 384 385 Stmt &Dassign(Expr src, Var &var, FieldId fieldId = 0); 386 Stmt &Iassign(Expr src, Expr addr, Type *baseType, FieldId fieldId = 0); 387 388 // expressions 389 Expr Dread(Var &var); // do we need other forms? Dread(Var * var)390 inline Expr Dread(Var *var) 391 { // shortcut for read from local-var 392 CHECK_NULL_FATAL(var); 393 return Dread(*var); 394 } 395 396 Expr IntrinsicOp(IntrinsicId id, Type *type, Args &args_); 397 Expr Iread(Type *type, Expr addr, Type *baseType, FieldId fieldId = 0); 398 PregIdx CreatePreg(Type *mtype); 399 Stmt &Regassign(Expr src, PregIdx reg); 400 Expr Regread(PregIdx pregIdx); 401 Expr ConstVal(Const &constVal); // a const operand 402 403 Expr Lnot(Type *type, Expr src); 404 Expr Bnot(Type *type, Expr src); 405 Expr Sqrt(Type *type, Expr src); 406 Expr Ceil(Type *type, Expr src); 407 Expr Abs(Type *type, Expr src); 408 409 Expr Add(Type *type, Expr src1, Expr src2); 410 Expr Sub(Type *type, Expr src1, Expr src2); 411 Expr Mul(Type *type, Expr src1, Expr src2); 412 Expr UDiv(Type *type, Expr src1, Expr src2); // unsigned 413 Expr SDiv(Type *type, Expr src1, Expr src2); // signed 414 Expr SRem(Type *type, Expr src1, Expr src2); // signed 415 Expr Shl(Type *type, Expr src1, Expr src2); 416 Expr LShr(Type *type, Expr src1, Expr src2); 417 Expr AShr(Type *type, Expr src1, Expr src2); 418 Expr And(Type *type, Expr src1, Expr src2); 419 Expr Or(Type *type, Expr src1, Expr src2); 420 Expr Xor(Type *type, Expr src1, Expr src2); 421 Expr Max(Type *type, Expr src1, Expr src2); 422 Expr Min(Type *type, Expr src1, Expr src2); 423 424 Expr ICmp(Type *type, Expr src1, Expr src2, IntCmpCondition cond); 425 Expr FCmp(Type *type, Expr src1, Expr src2, FloatCmpCondition cond); 426 427 // Type conversion 428 // Type of opnd should be consistent with fromType: no implicient conversion 429 Expr Trunc(Type *fromType, Type *toType, Expr opnd); 430 Expr ZExt(Type *fromType, Type *toType, Expr opnd); 431 Expr SExt(Type *fromType, Type *toType, Expr opnd); 432 Expr BitCast(Type *fromType, Type *toType, Expr opnd); 433 Expr Cvt(Type *fromType, Type *toType, Expr opnd); 434 Expr Floor(Type *fromType, Type *toType, Expr opnd); 435 Expr Ceil(Type *fromType, Type *toType, Expr opnd); 436 437 void SetFuncFrameResverdSlot(int slot); 438 void SetFuncFramePointer(const String &val); 439 void SetCurrentDebugComment(const std::string& comment); 440 void ClearCurrentDebugComment(); 441 442 public: 443 // helper classes for compound IR entity building 444 class SwitchBuilder { 445 public: SwitchBuilder(LMIRBuilder & builder_,Type * type_,Expr cond_,BB & defaultBB_)446 SwitchBuilder(LMIRBuilder &builder_, Type *type_, Expr cond_, BB &defaultBB_) 447 : builder(builder_), type(type_), cond(cond_), defaultBB(defaultBB_) 448 { 449 } 450 Case(int64_t value,BB & bb)451 SwitchBuilder &Case(int64_t value, BB &bb) 452 { 453 cases.push_back(std::make_pair(value, &bb)); 454 return *this; 455 } 456 Done()457 Stmt &Done() 458 { 459 return builder.CreateSwitchInternal(type, cond, defaultBB, cases); 460 } 461 462 private: 463 LMIRBuilder &builder; 464 Type *type; 465 Expr cond; 466 BB &defaultBB; 467 std::vector<std::pair<int64_t, BB *>> cases; 468 }; 469 Switch(Type * type,Expr cond,BB & defaultBB)470 SwitchBuilder Switch(Type *type, Expr cond, BB &defaultBB) 471 { 472 return SwitchBuilder(*this, type, cond, defaultBB); 473 } 474 475 class FunctionBuilder { 476 public: FunctionBuilder(LMIRBuilder & builder_,const String & name_,bool needBody_)477 FunctionBuilder(LMIRBuilder &builder_, const String &name_, bool needBody_) 478 : builder(builder_), name(name_), needBody(needBody_) 479 { 480 attr = FUNC_internal; 481 convAttr = CCall; 482 isVargs = false; 483 } 484 485 // optional: indicate the function has variable args Vargs()486 FunctionBuilder &Vargs() 487 { 488 isVargs = true; 489 return *this; 490 } 491 Param(Type * type,const String paramName)492 FunctionBuilder &Param(Type *type, const String paramName) 493 { 494 params.push_back(std::make_pair(paramName, type)); 495 return *this; 496 } 497 498 // optional: if not called, return nothing (return void) Return(Type * type)499 FunctionBuilder &Return(Type *type) 500 { 501 retType = type; 502 return *this; 503 } 504 505 // optional: if not called, default to FUNC_local Attribute(FuncAttr attr_)506 FunctionBuilder &Attribute(FuncAttr attr_) 507 { 508 attr = attr_; 509 return *this; 510 } 511 512 // optional: if not called, default to Func_CCall CallConvAttribute(ConvAttr convAttr_)513 FunctionBuilder &CallConvAttribute(ConvAttr convAttr_) 514 { 515 convAttr = convAttr_; 516 return *this; 517 } 518 Done()519 Function &Done() 520 { 521 return builder.CreateFunctionInternal(name, retType, params, isVargs, needBody, attr, convAttr); 522 } 523 524 private: 525 LMIRBuilder &builder; 526 const String &name; 527 Type *retType; 528 FuncAttr attr; 529 ConvAttr convAttr; 530 bool isVargs; 531 bool needBody; // indicate whether is a declaration or definition. 532 Params params; 533 }; 534 535 // only declare the function in current module (means it's an external function) DeclareFunction(const String & name)536 FunctionBuilder DeclareFunction(const String &name) 537 { 538 return FunctionBuilder(*this, name, false); 539 } 540 541 // define the function in current module DefineFunction(const String & name)542 FunctionBuilder DefineFunction(const String &name) 543 { 544 return FunctionBuilder(*this, name, true); 545 } 546 547 public: 548 // builtin types: primitive types (all MIR primitive types except PTY_ptr) 549 Type *i8Type; 550 Type *i16Type; 551 Type *i32Type; 552 Type *i64Type; 553 Type *u1Type; 554 Type *u8Type; 555 Type *u16Type; 556 Type *u32Type; 557 Type *u64Type; 558 Type *voidType; 559 Type *f32Type; 560 Type *f64Type; 561 562 // builtin types: commonly used derived types 563 Type *strType; 564 Type *i64PtrType; 565 Type *i64RefType; 566 Type *i64RefRefType; 567 568 private: 569 Stmt &CreateSwitchInternal(Type *type, Expr cond, BB &defaultBB, std::vector<std::pair<int64_t, BB *>> &cases); 570 void AddConstItemInternal(StructConst &structConst, FieldId fieldId, Const &field); 571 void AddConstItemInternal(ArrayConst &structConst, Const &element); 572 Function &CreateFunctionInternal(const String &name, Type *retType, Params ¶ms, bool isVargs, bool needBody, 573 FuncAttr attr, ConvAttr convAttr); 574 575 private: 576 MIRBuilder &mirBuilder; // The real IR-builder: current implementation 577 Module &module; // and the module to process 578 }; 579 580 } // namespace litecg 581 } // namespace maple 582 #endif // MAPLE_LITECG_LMIR_BUILDER_H 583