1// Copyright 2015 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/interpreter/interpreter-intrinsics.h" 6 7#include "src/code-factory.h" 8 9namespace v8 { 10namespace internal { 11namespace interpreter { 12 13using compiler::Node; 14 15#define __ assembler_-> 16 17IntrinsicsHelper::IntrinsicsHelper(InterpreterAssembler* assembler) 18 : isolate_(assembler->isolate()), 19 zone_(assembler->zone()), 20 assembler_(assembler) {} 21 22// static 23bool IntrinsicsHelper::IsSupported(Runtime::FunctionId function_id) { 24 switch (function_id) { 25#define SUPPORTED(name, lower_case, count) case Runtime::kInline##name: 26 INTRINSICS_LIST(SUPPORTED) 27 return true; 28#undef SUPPORTED 29 default: 30 return false; 31 } 32} 33 34// static 35IntrinsicsHelper::IntrinsicId IntrinsicsHelper::FromRuntimeId( 36 Runtime::FunctionId function_id) { 37 switch (function_id) { 38#define TO_RUNTIME_ID(name, lower_case, count) \ 39 case Runtime::kInline##name: \ 40 return IntrinsicId::k##name; 41 INTRINSICS_LIST(TO_RUNTIME_ID) 42#undef TO_RUNTIME_ID 43 default: 44 UNREACHABLE(); 45 return static_cast<IntrinsicsHelper::IntrinsicId>(-1); 46 } 47} 48 49// static 50Runtime::FunctionId IntrinsicsHelper::ToRuntimeId( 51 IntrinsicsHelper::IntrinsicId intrinsic_id) { 52 switch (intrinsic_id) { 53#define TO_INTRINSIC_ID(name, lower_case, count) \ 54 case IntrinsicId::k##name: \ 55 return Runtime::kInline##name; 56 INTRINSICS_LIST(TO_INTRINSIC_ID) 57#undef TO_INTRINSIC_ID 58 default: 59 UNREACHABLE(); 60 return static_cast<Runtime::FunctionId>(-1); 61 } 62} 63 64Node* IntrinsicsHelper::InvokeIntrinsic(Node* function_id, Node* context, 65 Node* first_arg_reg, Node* arg_count) { 66 InterpreterAssembler::Label abort(assembler_), end(assembler_); 67 InterpreterAssembler::Variable result(assembler_, 68 MachineRepresentation::kTagged); 69 70#define MAKE_LABEL(name, lower_case, count) \ 71 InterpreterAssembler::Label lower_case(assembler_); 72 INTRINSICS_LIST(MAKE_LABEL) 73#undef MAKE_LABEL 74 75#define LABEL_POINTER(name, lower_case, count) &lower_case, 76 InterpreterAssembler::Label* labels[] = {INTRINSICS_LIST(LABEL_POINTER)}; 77#undef LABEL_POINTER 78 79#define CASE(name, lower_case, count) \ 80 static_cast<int32_t>(IntrinsicId::k##name), 81 int32_t cases[] = {INTRINSICS_LIST(CASE)}; 82#undef CASE 83 84 __ Switch(function_id, &abort, cases, labels, arraysize(cases)); 85#define HANDLE_CASE(name, lower_case, expected_arg_count) \ 86 __ Bind(&lower_case); \ 87 if (FLAG_debug_code && expected_arg_count >= 0) { \ 88 AbortIfArgCountMismatch(expected_arg_count, arg_count); \ 89 } \ 90 result.Bind(name(first_arg_reg, arg_count, context)); \ 91 __ Goto(&end); 92 INTRINSICS_LIST(HANDLE_CASE) 93#undef HANDLE_CASE 94 95 __ Bind(&abort); 96 { 97 __ Abort(BailoutReason::kUnexpectedFunctionIDForInvokeIntrinsic); 98 result.Bind(__ UndefinedConstant()); 99 __ Goto(&end); 100 } 101 102 __ Bind(&end); 103 return result.value(); 104} 105 106Node* IntrinsicsHelper::CompareInstanceType(Node* object, int type, 107 InstanceTypeCompareMode mode) { 108 InterpreterAssembler::Variable return_value(assembler_, 109 MachineRepresentation::kTagged); 110 Node* instance_type = __ LoadInstanceType(object); 111 112 InterpreterAssembler::Label if_true(assembler_), if_false(assembler_), 113 end(assembler_); 114 if (mode == kInstanceTypeEqual) { 115 return __ Word32Equal(instance_type, __ Int32Constant(type)); 116 } else { 117 DCHECK(mode == kInstanceTypeGreaterThanOrEqual); 118 return __ Int32GreaterThanOrEqual(instance_type, __ Int32Constant(type)); 119 } 120} 121 122Node* IntrinsicsHelper::IsInstanceType(Node* input, int type) { 123 InterpreterAssembler::Variable return_value(assembler_, 124 MachineRepresentation::kTagged); 125 InterpreterAssembler::Label if_not_smi(assembler_), return_true(assembler_), 126 return_false(assembler_), end(assembler_); 127 Node* arg = __ LoadRegister(input); 128 __ GotoIf(__ TaggedIsSmi(arg), &return_false); 129 130 Node* condition = CompareInstanceType(arg, type, kInstanceTypeEqual); 131 __ Branch(condition, &return_true, &return_false); 132 133 __ Bind(&return_true); 134 { 135 return_value.Bind(__ BooleanConstant(true)); 136 __ Goto(&end); 137 } 138 139 __ Bind(&return_false); 140 { 141 return_value.Bind(__ BooleanConstant(false)); 142 __ Goto(&end); 143 } 144 145 __ Bind(&end); 146 return return_value.value(); 147} 148 149Node* IntrinsicsHelper::IsJSReceiver(Node* input, Node* arg_count, 150 Node* context) { 151 InterpreterAssembler::Variable return_value(assembler_, 152 MachineRepresentation::kTagged); 153 InterpreterAssembler::Label return_true(assembler_), return_false(assembler_), 154 end(assembler_); 155 156 Node* arg = __ LoadRegister(input); 157 __ GotoIf(__ TaggedIsSmi(arg), &return_false); 158 159 STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); 160 Node* condition = CompareInstanceType(arg, FIRST_JS_RECEIVER_TYPE, 161 kInstanceTypeGreaterThanOrEqual); 162 __ Branch(condition, &return_true, &return_false); 163 164 __ Bind(&return_true); 165 { 166 return_value.Bind(__ BooleanConstant(true)); 167 __ Goto(&end); 168 } 169 170 __ Bind(&return_false); 171 { 172 return_value.Bind(__ BooleanConstant(false)); 173 __ Goto(&end); 174 } 175 176 __ Bind(&end); 177 return return_value.value(); 178} 179 180Node* IntrinsicsHelper::IsArray(Node* input, Node* arg_count, Node* context) { 181 return IsInstanceType(input, JS_ARRAY_TYPE); 182} 183 184Node* IntrinsicsHelper::IsJSProxy(Node* input, Node* arg_count, Node* context) { 185 return IsInstanceType(input, JS_PROXY_TYPE); 186} 187 188Node* IntrinsicsHelper::IsRegExp(Node* input, Node* arg_count, Node* context) { 189 return IsInstanceType(input, JS_REGEXP_TYPE); 190} 191 192Node* IntrinsicsHelper::IsTypedArray(Node* input, Node* arg_count, 193 Node* context) { 194 return IsInstanceType(input, JS_TYPED_ARRAY_TYPE); 195} 196 197Node* IntrinsicsHelper::IsSmi(Node* input, Node* arg_count, Node* context) { 198 InterpreterAssembler::Variable return_value(assembler_, 199 MachineRepresentation::kTagged); 200 InterpreterAssembler::Label if_smi(assembler_), if_not_smi(assembler_), 201 end(assembler_); 202 203 Node* arg = __ LoadRegister(input); 204 205 __ Branch(__ TaggedIsSmi(arg), &if_smi, &if_not_smi); 206 __ Bind(&if_smi); 207 { 208 return_value.Bind(__ BooleanConstant(true)); 209 __ Goto(&end); 210 } 211 212 __ Bind(&if_not_smi); 213 { 214 return_value.Bind(__ BooleanConstant(false)); 215 __ Goto(&end); 216 } 217 218 __ Bind(&end); 219 return return_value.value(); 220} 221 222Node* IntrinsicsHelper::IntrinsicAsStubCall(Node* args_reg, Node* context, 223 Callable const& callable) { 224 int param_count = callable.descriptor().GetParameterCount(); 225 Node** args = zone()->NewArray<Node*>(param_count + 1); // 1 for context 226 for (int i = 0; i < param_count; i++) { 227 args[i] = __ LoadRegister(args_reg); 228 args_reg = __ NextRegister(args_reg); 229 } 230 args[param_count] = context; 231 232 return __ CallStubN(callable, args); 233} 234 235Node* IntrinsicsHelper::HasProperty(Node* input, Node* arg_count, 236 Node* context) { 237 return IntrinsicAsStubCall(input, context, 238 CodeFactory::HasProperty(isolate())); 239} 240 241Node* IntrinsicsHelper::NewObject(Node* input, Node* arg_count, Node* context) { 242 return IntrinsicAsStubCall(input, context, 243 CodeFactory::FastNewObject(isolate())); 244} 245 246Node* IntrinsicsHelper::NumberToString(Node* input, Node* arg_count, 247 Node* context) { 248 return IntrinsicAsStubCall(input, context, 249 CodeFactory::NumberToString(isolate())); 250} 251 252Node* IntrinsicsHelper::RegExpExec(Node* input, Node* arg_count, 253 Node* context) { 254 return IntrinsicAsStubCall(input, context, 255 CodeFactory::RegExpExec(isolate())); 256} 257 258Node* IntrinsicsHelper::SubString(Node* input, Node* arg_count, Node* context) { 259 return IntrinsicAsStubCall(input, context, CodeFactory::SubString(isolate())); 260} 261 262Node* IntrinsicsHelper::ToString(Node* input, Node* arg_count, Node* context) { 263 return IntrinsicAsStubCall(input, context, CodeFactory::ToString(isolate())); 264} 265 266Node* IntrinsicsHelper::ToLength(Node* input, Node* arg_count, Node* context) { 267 return IntrinsicAsStubCall(input, context, CodeFactory::ToLength(isolate())); 268} 269 270Node* IntrinsicsHelper::ToInteger(Node* input, Node* arg_count, Node* context) { 271 return IntrinsicAsStubCall(input, context, CodeFactory::ToInteger(isolate())); 272} 273 274Node* IntrinsicsHelper::ToNumber(Node* input, Node* arg_count, Node* context) { 275 return IntrinsicAsStubCall(input, context, CodeFactory::ToNumber(isolate())); 276} 277 278Node* IntrinsicsHelper::ToObject(Node* input, Node* arg_count, Node* context) { 279 return IntrinsicAsStubCall(input, context, CodeFactory::ToObject(isolate())); 280} 281 282Node* IntrinsicsHelper::Call(Node* args_reg, Node* arg_count, Node* context) { 283 // First argument register contains the function target. 284 Node* function = __ LoadRegister(args_reg); 285 286 // Receiver is the second runtime call argument. 287 Node* receiver_reg = __ NextRegister(args_reg); 288 Node* receiver_arg = __ RegisterLocation(receiver_reg); 289 290 // Subtract function and receiver from arg count. 291 Node* function_and_receiver_count = __ Int32Constant(2); 292 Node* target_args_count = __ Int32Sub(arg_count, function_and_receiver_count); 293 294 if (FLAG_debug_code) { 295 InterpreterAssembler::Label arg_count_positive(assembler_); 296 Node* comparison = __ Int32LessThan(target_args_count, __ Int32Constant(0)); 297 __ GotoUnless(comparison, &arg_count_positive); 298 __ Abort(kWrongArgumentCountForInvokeIntrinsic); 299 __ Goto(&arg_count_positive); 300 __ Bind(&arg_count_positive); 301 } 302 303 Node* result = __ CallJS(function, context, receiver_arg, target_args_count, 304 TailCallMode::kDisallow); 305 return result; 306} 307 308Node* IntrinsicsHelper::ValueOf(Node* args_reg, Node* arg_count, 309 Node* context) { 310 InterpreterAssembler::Variable return_value(assembler_, 311 MachineRepresentation::kTagged); 312 InterpreterAssembler::Label done(assembler_); 313 314 Node* object = __ LoadRegister(args_reg); 315 return_value.Bind(object); 316 317 // If the object is a smi return the object. 318 __ GotoIf(__ TaggedIsSmi(object), &done); 319 320 // If the object is not a value type, return the object. 321 Node* condition = 322 CompareInstanceType(object, JS_VALUE_TYPE, kInstanceTypeEqual); 323 __ GotoUnless(condition, &done); 324 325 // If the object is a value type, return the value field. 326 return_value.Bind(__ LoadObjectField(object, JSValue::kValueOffset)); 327 __ Goto(&done); 328 329 __ Bind(&done); 330 return return_value.value(); 331} 332 333Node* IntrinsicsHelper::ClassOf(Node* args_reg, Node* arg_count, 334 Node* context) { 335 InterpreterAssembler::Variable return_value(assembler_, 336 MachineRepresentation::kTagged); 337 InterpreterAssembler::Label done(assembler_), null(assembler_), 338 function(assembler_), non_function_constructor(assembler_); 339 340 Node* object = __ LoadRegister(args_reg); 341 342 // If the object is not a JSReceiver, we return null. 343 __ GotoIf(__ TaggedIsSmi(object), &null); 344 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); 345 Node* is_js_receiver = CompareInstanceType(object, FIRST_JS_RECEIVER_TYPE, 346 kInstanceTypeGreaterThanOrEqual); 347 __ GotoUnless(is_js_receiver, &null); 348 349 // Return 'Function' for JSFunction and JSBoundFunction objects. 350 Node* is_function = CompareInstanceType(object, FIRST_FUNCTION_TYPE, 351 kInstanceTypeGreaterThanOrEqual); 352 STATIC_ASSERT(LAST_FUNCTION_TYPE == LAST_TYPE); 353 __ GotoIf(is_function, &function); 354 355 // Check if the constructor in the map is a JS function. 356 Node* constructor = __ LoadMapConstructor(__ LoadMap(object)); 357 Node* constructor_is_js_function = 358 CompareInstanceType(constructor, JS_FUNCTION_TYPE, kInstanceTypeEqual); 359 __ GotoUnless(constructor_is_js_function, &non_function_constructor); 360 361 // Grab the instance class name from the constructor function. 362 Node* shared = 363 __ LoadObjectField(constructor, JSFunction::kSharedFunctionInfoOffset); 364 return_value.Bind( 365 __ LoadObjectField(shared, SharedFunctionInfo::kInstanceClassNameOffset)); 366 __ Goto(&done); 367 368 // Non-JS objects have class null. 369 __ Bind(&null); 370 { 371 return_value.Bind(__ LoadRoot(Heap::kNullValueRootIndex)); 372 __ Goto(&done); 373 } 374 375 // Functions have class 'Function'. 376 __ Bind(&function); 377 { 378 return_value.Bind(__ LoadRoot(Heap::kFunction_stringRootIndex)); 379 __ Goto(&done); 380 } 381 382 // Objects with a non-function constructor have class 'Object'. 383 __ Bind(&non_function_constructor); 384 { 385 return_value.Bind(__ LoadRoot(Heap::kObject_stringRootIndex)); 386 __ Goto(&done); 387 } 388 389 __ Bind(&done); 390 return return_value.value(); 391} 392 393void IntrinsicsHelper::AbortIfArgCountMismatch(int expected, Node* actual) { 394 InterpreterAssembler::Label match(assembler_); 395 Node* comparison = __ Word32Equal(actual, __ Int32Constant(expected)); 396 __ GotoIf(comparison, &match); 397 __ Abort(kWrongArgumentCountForInvokeIntrinsic); 398 __ Goto(&match); 399 __ Bind(&match); 400} 401 402} // namespace interpreter 403} // namespace internal 404} // namespace v8 405