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 #include "class_init.h"
17 #include <iostream>
18 #include <fstream>
19
20 namespace {
21 constexpr char kCinfString[] = "__cinf_Ljava_2Flang_2FString_3B";
22 constexpr char kINFOFilename[] = "INFO_filename";
23 constexpr char kCoreAll[] = "core-all";
24 constexpr char kMCCPreClinitCheck[] = "MCC_PreClinitCheck";
25 constexpr char kMCCPostClinitCheck[] = "MCC_PostClinitCheck";
26 } // namespace
27
28 // This phase does two things.
29 // 1. Insert clinit(class initialization) check, a intrinsic call INTRN_MPL_CLINIT_CHECK
30 // for place where needed.
31 // Insert clinit check for static native methods which are not private.
32 // 2. Lower JAVA_CLINIT_CHECK to MPL_CLINIT_CHECK.
33 // Before insert or tranform the clinit check, we used a optimise based on
34 // white list. When the dexname is core-all and the class is in the white list
35 // we dont't insert clinit check.Because the class in the white list is intialized
36 // in the system bootup.
37 namespace maple {
CanRemoveClinitCheck(const std::string & clinitClassname) const38 bool ClassInit::CanRemoveClinitCheck(const std::string &clinitClassname) const
39 {
40 if (!Options::usePreloadedClass) {
41 return false;
42 }
43 if (clinitClassname.empty()) {
44 return false;
45 }
46 if (clinitClassname == kCinfString) {
47 return true;
48 }
49 uint32 dexNameIdx =
50 GetMIRModule().GetFileinfo(GlobalTables::GetStrTable().GetOrCreateStrIdxFromName(kINFOFilename));
51 const std::string &dexName = GlobalTables::GetStrTable().GetStringFromStrIdx(GStrIdx(dexNameIdx));
52 if (dexName.find(kCoreAll) != std::string::npos) {
53 return false;
54 }
55 return IsSystemPreloadedClass(clinitClassname);
56 }
57
GenClassInitCheckProfile(MIRFunction & func,const MIRSymbol & classInfo,StmtNode * clinit) const58 void ClassInit::GenClassInitCheckProfile(MIRFunction &func, const MIRSymbol &classInfo, StmtNode *clinit) const
59 {
60 GenPreClassInitCheck(func, classInfo, clinit);
61 GenPostClassInitCheck(func, classInfo, clinit);
62 }
63
GenPreClassInitCheck(MIRFunction & func,const MIRSymbol & classInfo,const StmtNode * clinit) const64 void ClassInit::GenPreClassInitCheck(MIRFunction &func, const MIRSymbol &classInfo, const StmtNode *clinit) const
65 {
66 MIRFunction *preClinit = builder->GetOrCreateFunction(kMCCPreClinitCheck, (TyIdx)(PTY_void));
67 BaseNode *classInfoNode = builder->CreateExprAddrof(0, classInfo);
68 MapleVector<BaseNode *> args(builder->GetCurrentFuncCodeMpAllocator()->Adapter());
69 args.push_back(classInfoNode);
70 CallNode *callPreclinit = builder->CreateStmtCall(preClinit->GetPuidx(), args);
71 func.GetBody()->InsertBefore(clinit, callPreclinit);
72 }
73
GenPostClassInitCheck(MIRFunction & func,const MIRSymbol & classInfo,const StmtNode * clinit) const74 void ClassInit::GenPostClassInitCheck(MIRFunction &func, const MIRSymbol &classInfo, const StmtNode *clinit) const
75 {
76 MIRFunction *postClinit = builder->GetOrCreateFunction(kMCCPostClinitCheck, (TyIdx)(PTY_void));
77 BaseNode *classInfoNode = builder->CreateExprAddrof(0, classInfo);
78 MapleVector<BaseNode *> args(builder->GetCurrentFuncCodeMpAllocator()->Adapter());
79 args.push_back(classInfoNode);
80 CallNode *callPostClinit = builder->CreateStmtCall(postClinit->GetPuidx(), args);
81 func.GetBody()->InsertAfter(clinit, callPostClinit);
82 }
83
ProcessFunc(MIRFunction * func)84 void ClassInit::ProcessFunc(MIRFunction *func)
85 {
86 // No field will be involved in critical native funcs.
87 DEBUG_ASSERT(func != nullptr, "null ptr check!");
88 if (func->IsEmpty() || func->GetAttr(FUNCATTR_critical_native)) {
89 return;
90 }
91 currFunc = func;
92 builder->SetCurrentFunction(*func);
93 // Insert clinit check for static methods.
94 MIRType *selfClassType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(func->GetClassTyIdx());
95 std::string selfClassName;
96 if (selfClassType != nullptr) {
97 selfClassName = GlobalTables::GetStrTable().GetStringFromStrIdx(selfClassType->GetNameStrIdx());
98 } else {
99 const std::string &funcName = func->GetName();
100 size_t pos = funcName.find(namemangler::kNameSplitterStr);
101 constexpr size_t prePos = 2;
102 constexpr size_t ligalPos = 2;
103 while (pos != std::string::npos &&
104 (pos >= ligalPos && funcName[pos - 1] == '_' && funcName[pos - prePos] != '_')) {
105 constexpr size_t nextPos = 3;
106 pos = funcName.find(namemangler::kNameSplitterStr, pos + nextPos);
107 }
108 selfClassName = funcName.substr(0, pos);
109 }
110 // Insert clinit check for static native methods which are not private.
111 // We have to do this here because native methods are generated as empty by maplefe,
112 // If we simply insert clinit-check (which does not have return value), there will
113 // be no return statement for native methods which do hava a return value.
114 // clinit check for static java (non-native) methods which are not private is
115 // already inserted by maplefe.
116 if (func->IsStatic() && !func->IsPrivate() && !func->IsClinit() && func->IsNative()) {
117 MIRType *classType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(func->GetClassTyIdx());
118 CHECK_FATAL(classType != nullptr, "class type is nullptr");
119 const std::string &className = GlobalTables::GetStrTable().GetStringFromStrIdx(classType->GetNameStrIdx());
120 if (!CanRemoveClinitCheck(className)) {
121 Klass *klass = klassHierarchy->GetKlassFromName(className);
122 CHECK_FATAL(klass != nullptr, "klass is nullptr in ClassInit::ProcessFunc");
123 if (klass->GetClinit() && func != klass->GetClinit()) {
124 MIRSymbol *classInfo = GetClassInfo(className);
125 BaseNode *classInfoNode = builder->CreateExprAddrof(0, *classInfo);
126 if (trace) {
127 LogInfo::MapleLogger() << "\t- low-cost clinit - insert check in static method " << func->GetName()
128 << "clasname " << className << "\n";
129 }
130 MapleVector<BaseNode *> args(builder->GetCurrentFuncCodeMpAllocator()->Adapter());
131 args.push_back(classInfoNode);
132 StmtNode *intrinsicCall = builder->CreateStmtIntrinsicCall(INTRN_MPL_CLINIT_CHECK, args);
133 func->GetBody()->InsertFirst(intrinsicCall);
134 DEBUG_ASSERT(classInfo != nullptr, "null ptr check!");
135 #ifdef CLINIT_CHECK
136 GenClassInitCheckProfile(*func, *classInfo, intrinsicCall);
137 #endif // CLINIT_CHECK
138 }
139 }
140 }
141 // Lower JAVA_CLINIT_CHECK to MPL_CLINIT_CHECK.
142 StmtNode *stmt = func->GetBody()->GetFirst();
143 while (stmt != nullptr) {
144 if (stmt->GetOpCode() == OP_intrinsiccallwithtype) {
145 auto *intrinsicCall = static_cast<IntrinsiccallNode *>(stmt);
146 if (intrinsicCall->GetIntrinsic() == INTRN_JAVA_CLINIT_CHECK) {
147 // intrinsiccallwithtype <$LTest_3B> JAVA_CLINIT_CHECK () -->
148 // intrinsiccall MPL_CLINIT_CHECK (addrof ptr $__cinf_LTest_3B)
149 CHECK_FATAL(intrinsicCall->GetNopndSize() == 0, "wrong arg vectors");
150 CHECK_FATAL(intrinsicCall->GetTyIdx() < GlobalTables::GetTypeTable().GetTypeTable().size(),
151 "index out of range");
152 MIRType *classType = GlobalTables::GetTypeTable().GetTypeTable()[intrinsicCall->GetTyIdx()];
153 DEBUG_ASSERT(classType != nullptr, "null ptr check!");
154 CHECK_FATAL(classType->GetNameStrIdx() != 0u, "symbol name is null for type index %d",
155 static_cast<uint32>(intrinsicCall->GetTyIdx()));
156 const std::string &className =
157 GlobalTables::GetStrTable().GetStringFromStrIdx(classType->GetNameStrIdx());
158 Klass *klass = klassHierarchy->GetKlassFromName(className);
159 bool doClinitCheck = false;
160 if (klass == nullptr) {
161 WARN(kLncWarn, "ClassInit::ProcessFunc: Skip INCOMPLETE type %s", className.c_str());
162 doClinitCheck = true;
163 } else {
164 doClinitCheck =
165 !CanRemoveClinitCheck(className) && klassHierarchy->NeedClinitCheckRecursively(*klass);
166 }
167 if (Options::buildApp != 0) {
168 doClinitCheck = true;
169 }
170 if (doClinitCheck) {
171 MIRSymbol *classInfo = GetClassInfo(className);
172 AddrofNode *classInfoNode = builder->CreateExprAddrof(0, *classInfo);
173 MapleVector<BaseNode *> args(builder->GetCurrentFuncCodeMpAllocator()->Adapter());
174 args.push_back(classInfoNode);
175 StmtNode *mplIntrinsicCall = builder->CreateStmtIntrinsicCall(INTRN_MPL_CLINIT_CHECK, args);
176 func->GetBody()->ReplaceStmt1WithStmt2(stmt, mplIntrinsicCall);
177 if (trace) {
178 LogInfo::MapleLogger() << "\t- low-cost clinit - lower JAVA_CLINIT_CHECK " << className
179 << " in " << func->GetName() << "()\n";
180 }
181 DEBUG_ASSERT(classInfo != nullptr, "null ptr check!");
182 #ifdef CLINIT_CHECK
183 GenClassInitCheckProfile(*func, *classInfo, mplIntrinsicCall);
184 #endif // CLINIT_CHECK
185 } else {
186 func->GetBody()->RemoveStmt(stmt);
187 }
188 }
189 }
190 stmt = stmt->GetNext();
191 }
192 }
193
GetClassInfo(const std::string & classname)194 MIRSymbol *ClassInit::GetClassInfo(const std::string &classname)
195 {
196 const std::string &classInfoName = CLASSINFO_PREFIX_STR + classname;
197 MIRType *classInfoType =
198 GlobalTables::GetTypeTable().GetOrCreateClassType(namemangler::kClassMetadataTypeName, GetMIRModule());
199 MIRSymbol *classInfo = builder->GetOrCreateGlobalDecl(classInfoName, *classInfoType);
200 Klass *klass = klassHierarchy->GetKlassFromName(classname);
201 if (klass == nullptr || !klass->GetMIRStructType()->IsLocal()) {
202 classInfo->SetStorageClass(kScExtern);
203 }
204 return classInfo;
205 }
206
207 } // namespace maple
208