diff --git a/runtime/vm/compiler/asm_intrinsifier_arm.cc b/runtime/vm/compiler/asm_intrinsifier_arm.cc index 770e7ff6883..5bd61d7c08a 100644 --- a/runtime/vm/compiler/asm_intrinsifier_arm.cc +++ b/runtime/vm/compiler/asm_intrinsifier_arm.cc @@ -1081,33 +1081,27 @@ void AsmIntrinsifier::ObjectRuntimeType(Assembler* assembler, __ CompareImmediate(R1, kNumPredefinedCids); __ b(&use_declaration_type, HI); + __ LoadIsolateGroup(R2); + __ LoadFromOffset(R2, R2, target::IsolateGroup::object_store_offset()); + __ CompareImmediate(R1, kDoubleCid); __ b(¬_double, NE); - - __ LoadIsolateGroup(R0); - __ LoadFromOffset(R0, R0, target::IsolateGroup::object_store_offset()); - __ LoadFromOffset(R0, R0, target::ObjectStore::double_type_offset()); + __ LoadFromOffset(R0, R2, target::ObjectStore::double_type_offset()); __ Ret(); __ Bind(¬_double); JumpIfNotInteger(assembler, R1, R0, ¬_integer); - __ LoadIsolateGroup(R0); - __ LoadFromOffset(R0, R0, target::IsolateGroup::object_store_offset()); - __ LoadFromOffset(R0, R0, target::ObjectStore::int_type_offset()); + __ LoadFromOffset(R0, R2, target::ObjectStore::int_type_offset()); __ Ret(); __ Bind(¬_integer); JumpIfNotString(assembler, R1, R0, ¬_string); - __ LoadIsolateGroup(R0); - __ LoadFromOffset(R0, R0, target::IsolateGroup::object_store_offset()); - __ LoadFromOffset(R0, R0, target::ObjectStore::string_type_offset()); + __ LoadFromOffset(R0, R2, target::ObjectStore::string_type_offset()); __ Ret(); __ Bind(¬_string); JumpIfNotType(assembler, R1, R0, &use_declaration_type); - __ LoadIsolateGroup(R0); - __ LoadFromOffset(R0, R0, target::IsolateGroup::object_store_offset()); - __ LoadFromOffset(R0, R0, target::ObjectStore::type_type_offset()); + __ LoadFromOffset(R0, R2, target::ObjectStore::type_type_offset()); __ Ret(); __ Bind(&use_declaration_type); diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc index 570c8448adf..2cd0d6ebced 100644 --- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc +++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc @@ -1021,7 +1021,7 @@ void AsmIntrinsifier::Double_mulFromInteger(Assembler* assembler, __ LoadDFieldFromOffset(V0, R0, target::Double::value_offset()); __ fmuld(V0, V0, V1); const Class& double_class = DoubleClass(); - __ TryAllocate(double_class, normal_ir_body, Assembler::kFarJump, R0, R1); + __ TryAllocate(double_class, normal_ir_body, Assembler::kNearJump, R0, R1); __ StoreDFieldToOffset(V0, R0, target::Double::value_offset()); __ ret(); __ Bind(normal_ir_body); @@ -1039,7 +1039,7 @@ void AsmIntrinsifier::DoubleFromInteger(Assembler* assembler, __ scvtfdw(V0, R0); #endif const Class& double_class = DoubleClass(); - __ TryAllocate(double_class, normal_ir_body, Assembler::kFarJump, R0, R1); + __ TryAllocate(double_class, normal_ir_body, Assembler::kNearJump, R0, R1); __ StoreDFieldToOffset(V0, R0, target::Double::value_offset()); __ ret(); __ Bind(normal_ir_body); @@ -1243,33 +1243,27 @@ void AsmIntrinsifier::ObjectRuntimeType(Assembler* assembler, __ CompareImmediate(R1, kNumPredefinedCids); __ b(&use_declaration_type, HI); + __ LoadIsolateGroup(R2); + __ LoadFromOffset(R2, R2, target::IsolateGroup::object_store_offset()); + __ CompareImmediate(R1, kDoubleCid); __ b(¬_double, NE); - - __ LoadIsolateGroup(R0); - __ LoadFromOffset(R0, R0, target::IsolateGroup::object_store_offset()); - __ LoadFromOffset(R0, R0, target::ObjectStore::double_type_offset()); + __ LoadFromOffset(R0, R2, target::ObjectStore::double_type_offset()); __ ret(); __ Bind(¬_double); JumpIfNotInteger(assembler, R1, R0, ¬_integer); - __ LoadIsolateGroup(R0); - __ LoadFromOffset(R0, R0, target::IsolateGroup::object_store_offset()); - __ LoadFromOffset(R0, R0, target::ObjectStore::int_type_offset()); + __ LoadFromOffset(R0, R2, target::ObjectStore::int_type_offset()); __ ret(); __ Bind(¬_integer); JumpIfNotString(assembler, R1, R0, ¬_string); - __ LoadIsolateGroup(R0); - __ LoadFromOffset(R0, R0, target::IsolateGroup::object_store_offset()); - __ LoadFromOffset(R0, R0, target::ObjectStore::string_type_offset()); + __ LoadFromOffset(R0, R2, target::ObjectStore::string_type_offset()); __ ret(); __ Bind(¬_string); JumpIfNotType(assembler, R1, R0, &use_declaration_type); - __ LoadIsolateGroup(R0); - __ LoadFromOffset(R0, R0, target::IsolateGroup::object_store_offset()); - __ LoadFromOffset(R0, R0, target::ObjectStore::type_type_offset()); + __ LoadFromOffset(R0, R2, target::ObjectStore::type_type_offset()); __ ret(); __ Bind(&use_declaration_type); @@ -1278,8 +1272,7 @@ void AsmIntrinsifier::ObjectRuntimeType(Assembler* assembler, R3, FieldAddress(R2, target::Class::num_type_arguments_offset(), kTwoBytes), kTwoBytes); - __ CompareImmediate(R3, 0); - __ b(normal_ir_body, NE); + __ cbnz(normal_ir_body, R3); __ LoadCompressed(R0, FieldAddress(R2, target::Class::declaration_type_offset())); diff --git a/runtime/vm/compiler/asm_intrinsifier_riscv.cc b/runtime/vm/compiler/asm_intrinsifier_riscv.cc index f1726081325..a1b94de3ff1 100644 --- a/runtime/vm/compiler/asm_intrinsifier_riscv.cc +++ b/runtime/vm/compiler/asm_intrinsifier_riscv.cc @@ -1060,7 +1060,23 @@ void AsmIntrinsifier::Double_div(Assembler* assembler, Label* normal_ir_body) { // Left is double, right is integer (Mint or Smi) void AsmIntrinsifier::Double_mulFromInteger(Assembler* assembler, Label* normal_ir_body) { - // TODO(riscv) + // Only smis allowed. + __ lx(A1, Address(SP, 0 * target::kWordSize)); + __ BranchIfNotSmi(A1, normal_ir_body, Assembler::kNearJump); + // Is Smi. + __ SmiUntag(A1); +#if XLEN == 32 + __ fcvtdw(FA1, A1); +#else + __ fcvtdl(FA1, A1); +#endif + __ lx(A0, Address(SP, 1 * target::kWordSize)); + __ LoadDFieldFromOffset(FA0, A0, target::Double::value_offset()); + __ fmuld(FA0, FA0, FA1); + const Class& double_class = DoubleClass(); + __ TryAllocate(double_class, normal_ir_body, Assembler::kNearJump, A0, A1); + __ StoreDFieldToOffset(FA0, A0, target::Double::value_offset()); + __ ret(); __ Bind(normal_ir_body); } @@ -1076,7 +1092,7 @@ void AsmIntrinsifier::DoubleFromInteger(Assembler* assembler, __ fcvtdl(FA0, A0); #endif const Class& double_class = DoubleClass(); - __ TryAllocate(double_class, normal_ir_body, Assembler::kFarJump, A0, TMP); + __ TryAllocate(double_class, normal_ir_body, Assembler::kNearJump, A0, TMP); __ StoreDFieldToOffset(FA0, A0, target::Double::value_offset()); __ ret(); __ Bind(normal_ir_body); @@ -1114,7 +1130,40 @@ void AsmIntrinsifier::Double_getIsNegative(Assembler* assembler, void AsmIntrinsifier::Double_hashCode(Assembler* assembler, Label* normal_ir_body) { - // TODO(riscv) + Label double_hash; + + __ lx(A0, Address(SP, 0 * target::kWordSize)); + __ LoadDFieldFromOffset(FA0, A0, target::Double::value_offset()); + +#if XLEN == 32 + __ fcvtwd(A1, FA0); + __ fcvtdw(FA1, A1); +#else + __ fcvtld(A1, FA0); + __ fcvtdl(FA1, A1); +#endif + __ feqd(TMP, FA0, FA1); + __ beqz(TMP, &double_hash, Assembler::kNearJump); // Not integer. + + __ SmiTag(A0, A1); + __ SmiUntag(TMP, A0); + __ bne(TMP, A1, normal_ir_body, Assembler::kNearJump); // Not Smi. + __ ret(); + + __ Bind(&double_hash); +#if XLEN == 32 + __ lx(A0, Address(SP, 0 * target::kWordSize)); + __ lw(A1, Address(A0, target::Double::value_offset() + 4)); + __ lw(A0, Address(A0, target::Double::value_offset() + 0)); +#else + __ fmvxd(A0, FA0); + __ srli(A1, A0, 32); +#endif + __ xor_(A0, A0, A1); + __ AndImmediate(A0, A0, target::kSmiMax); + __ SmiTag(A0); + __ ret(); + __ Bind(normal_ir_body); } @@ -1131,16 +1180,239 @@ void AsmIntrinsifier::ObjectEquals(Assembler* assembler, __ ret(); } +static void RangeCheck(Assembler* assembler, + Register val, + Register tmp, + intptr_t low, + intptr_t high, + Condition cc, + Label* target) { + __ AddImmediate(tmp, val, -low); + __ CompareImmediate(tmp, high - low); + __ BranchIf(cc, target); +} + +const Condition kIfNotInRange = HI; +const Condition kIfInRange = LS; + +static void JumpIfInteger(Assembler* assembler, + Register cid, + Register tmp, + Label* target) { + RangeCheck(assembler, cid, tmp, kSmiCid, kMintCid, kIfInRange, target); +} + +static void JumpIfNotInteger(Assembler* assembler, + Register cid, + Register tmp, + Label* target) { + RangeCheck(assembler, cid, tmp, kSmiCid, kMintCid, kIfNotInRange, target); +} + +static void JumpIfString(Assembler* assembler, + Register cid, + Register tmp, + Label* target) { + RangeCheck(assembler, cid, tmp, kOneByteStringCid, kExternalTwoByteStringCid, + kIfInRange, target); +} + +static void JumpIfNotString(Assembler* assembler, + Register cid, + Register tmp, + Label* target) { + RangeCheck(assembler, cid, tmp, kOneByteStringCid, kExternalTwoByteStringCid, + kIfNotInRange, target); +} + +static void JumpIfNotList(Assembler* assembler, + Register cid, + Register tmp, + Label* target) { + RangeCheck(assembler, cid, tmp, kArrayCid, kGrowableObjectArrayCid, + kIfNotInRange, target); +} + +static void JumpIfType(Assembler* assembler, + Register cid, + Register tmp, + Label* target) { + RangeCheck(assembler, cid, tmp, kTypeCid, kFunctionTypeCid, kIfInRange, + target); +} + +static void JumpIfNotType(Assembler* assembler, + Register cid, + Register tmp, + Label* target) { + RangeCheck(assembler, cid, tmp, kTypeCid, kFunctionTypeCid, kIfNotInRange, + target); +} + // Return type quickly for simple types (not parameterized and not signature). void AsmIntrinsifier::ObjectRuntimeType(Assembler* assembler, Label* normal_ir_body) { - // TODO(riscv) + Label use_declaration_type, not_double, not_integer, not_string; + __ lx(A0, Address(SP, 0 * target::kWordSize)); + __ LoadClassIdMayBeSmi(A1, A0); + + __ CompareImmediate(A1, kClosureCid); + __ BranchIf(EQ, normal_ir_body); // Instance is a closure. + + __ CompareImmediate(A1, kNumPredefinedCids); + __ BranchIf(HI, &use_declaration_type, Assembler::kNearJump); + + __ LoadIsolateGroup(A0); + __ LoadFromOffset(A0, A0, target::IsolateGroup::object_store_offset()); + + __ CompareImmediate(A1, kDoubleCid); + __ BranchIf(NE, ¬_double, Assembler::kNearJump); + __ LoadFromOffset(A0, A0, target::ObjectStore::double_type_offset()); + __ ret(); + + __ Bind(¬_double); + JumpIfNotInteger(assembler, A1, TMP, ¬_integer); + __ LoadFromOffset(A0, A0, target::ObjectStore::int_type_offset()); + __ ret(); + + __ Bind(¬_integer); + JumpIfNotString(assembler, A1, TMP, ¬_string); + __ LoadFromOffset(A0, A0, target::ObjectStore::string_type_offset()); + __ ret(); + + __ Bind(¬_string); + JumpIfNotType(assembler, A1, TMP, &use_declaration_type); + __ LoadFromOffset(A0, A0, target::ObjectStore::type_type_offset()); + __ ret(); + + __ Bind(&use_declaration_type); + __ LoadClassById(T2, A1); + __ lh(T3, FieldAddress(T2, target::Class::num_type_arguments_offset())); + __ bnez(T3, normal_ir_body, Assembler::kNearJump); + + __ LoadCompressed(A0, + FieldAddress(T2, target::Class::declaration_type_offset())); + __ beq(A0, NULL_REG, normal_ir_body, Assembler::kNearJump); + __ ret(); + __ Bind(normal_ir_body); } +// Compares cid1 and cid2 to see if they're syntactically equivalent. If this +// can be determined by this fast path, it jumps to either equal_* or not_equal. +// If classes are equivalent but may be generic, then jumps to +// equal_may_be_generic. Clobbers scratch. +static void EquivalentClassIds(Assembler* assembler, + Label* normal_ir_body, + Label* equal_may_be_generic, + Label* equal_not_generic, + Label* not_equal, + Register cid1, + Register cid2, + Register scratch, + bool testing_instance_cids) { + Label not_integer, not_integer_or_string, not_integer_or_string_or_list; + + // Check if left hand side is a closure. Closures are handled in the runtime. + __ CompareImmediate(cid1, kClosureCid); + __ BranchIf(EQ, normal_ir_body); + + // Check whether class ids match. If class ids don't match types may still be + // considered equivalent (e.g. multiple string implementation classes map to a + // single String type). + __ beq(cid1, cid2, equal_may_be_generic); + + // Class ids are different. Check if we are comparing two string types (with + // different representations), two integer types, two list types or two type + // types. + __ CompareImmediate(cid1, kNumPredefinedCids); + __ BranchIf(HI, not_equal); + + // Check if both are integer types. + JumpIfNotInteger(assembler, cid1, scratch, ¬_integer); + + // First type is an integer. Check if the second is an integer too. + JumpIfInteger(assembler, cid2, scratch, equal_not_generic); + // Integer types are only equivalent to other integer types. + __ j(not_equal, Assembler::kNearJump); + + __ Bind(¬_integer); + // Check if both are String types. + JumpIfNotString(assembler, cid1, scratch, + testing_instance_cids ? ¬_integer_or_string : not_equal); + + // First type is String. Check if the second is a string too. + JumpIfString(assembler, cid2, scratch, equal_not_generic); + // String types are only equivalent to other String types. + __ j(not_equal, Assembler::kNearJump); + + if (testing_instance_cids) { + __ Bind(¬_integer_or_string); + // Check if both are List types. + JumpIfNotList(assembler, cid1, scratch, ¬_integer_or_string_or_list); + + // First type is a List. Check if the second is a List too. + JumpIfNotList(assembler, cid2, scratch, not_equal); + ASSERT(compiler::target::Array::type_arguments_offset() == + compiler::target::GrowableObjectArray::type_arguments_offset()); + __ j(equal_may_be_generic, Assembler::kNearJump); + + __ Bind(¬_integer_or_string_or_list); + // Check if the first type is a Type. If it is not then types are not + // equivalent because they have different class ids and they are not String + // or integer or List or Type. + JumpIfNotType(assembler, cid1, scratch, not_equal); + + // First type is a Type. Check if the second is a Type too. + JumpIfType(assembler, cid2, scratch, equal_not_generic); + // Type types are only equivalent to other Type types. + __ j(not_equal, Assembler::kNearJump); + } +} + void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler, Label* normal_ir_body) { - // TODO(riscv) + __ lx(A0, Address(SP, 1 * target::kWordSize)); + __ lx(A1, Address(SP, 0 * target::kWordSize)); + __ LoadClassIdMayBeSmi(T2, A1); + __ LoadClassIdMayBeSmi(A1, A0); + + Label equal_may_be_generic, equal, not_equal; + EquivalentClassIds(assembler, normal_ir_body, &equal_may_be_generic, &equal, + ¬_equal, A1, T2, TMP, + /* testing_instance_cids = */ true); + + __ Bind(&equal_may_be_generic); + // Classes are equivalent and neither is a closure class. + // Check if there are no type arguments. In this case we can return true. + // Otherwise fall through into the runtime to handle comparison. + __ LoadClassById(A0, A1); + __ lw(T0, + FieldAddress( + A0, + target::Class::host_type_arguments_field_offset_in_words_offset())); + __ CompareImmediate(T0, target::Class::kNoTypeArguments); + __ BranchIf(EQ, &equal, Assembler::kNearJump); + + // Compare type arguments, host_type_arguments_field_offset_in_words in A0. + __ lx(A0, Address(SP, 1 * target::kWordSize)); + __ lx(A1, Address(SP, 0 * target::kWordSize)); + __ slli(T0, T0, target::kCompressedWordSizeLog2); + __ add(A0, A0, T0); + __ add(A1, A1, T0); + __ lx(A0, FieldAddress(A0, 0)); + __ lx(A1, FieldAddress(A1, 0)); + __ bne(A0, A1, normal_ir_body, Assembler::kNearJump); + // Fall through to equal case if type arguments are equal. + + __ Bind(&equal); + __ LoadObject(A0, CastHandle(TrueObject())); + __ Ret(); + + __ Bind(¬_equal); + __ LoadObject(A0, CastHandle(FalseObject())); + __ ret(); + __ Bind(normal_ir_body); } @@ -1175,7 +1447,60 @@ void AsmIntrinsifier::Type_getHashCode(Assembler* assembler, void AsmIntrinsifier::Type_equality(Assembler* assembler, Label* normal_ir_body) { - // TODO(riscv) + Label equal, not_equal, equiv_cids_may_be_generic, equiv_cids, check_legacy; + + __ lx(A0, Address(SP, 1 * target::kWordSize)); + __ lx(A1, Address(SP, 0 * target::kWordSize)); + __ beq(A1, A0, &equal); + + // A1 might not be a Type object, so check that first (A0 should be though, + // since this is a method on the Type class). + __ LoadClassIdMayBeSmi(T3, A1); + __ CompareImmediate(T3, kTypeCid); + __ BranchIf(NE, normal_ir_body); + + // Check if types are syntactically equal. + __ LoadTypeClassId(T3, A1); + __ LoadTypeClassId(T4, A0); + // We are not testing instance cids, but type class cids of Type instances. + EquivalentClassIds(assembler, normal_ir_body, &equiv_cids_may_be_generic, + &equiv_cids, ¬_equal, T3, T4, TMP, + /* testing_instance_cids = */ false); + + __ Bind(&equiv_cids_may_be_generic); + // Compare type arguments in Type instances. + __ LoadCompressed(T3, FieldAddress(A1, target::Type::arguments_offset())); + __ LoadCompressed(T4, FieldAddress(A0, target::Type::arguments_offset())); + __ CompareObjectRegisters(T3, T4); + __ BranchIf(NE, normal_ir_body); + // Fall through to check nullability if type arguments are equal. + + // Check nullability. + __ Bind(&equiv_cids); + __ lbu(A0, FieldAddress(A0, target::Type::nullability_offset())); + __ lbu(A1, FieldAddress(A1, target::Type::nullability_offset())); + __ bne(A0, A1, &check_legacy); + // Fall through to equal case if nullability is strictly equal. + + __ Bind(&equal); + __ LoadObject(A0, CastHandle(TrueObject())); + __ ret(); + + // At this point the nullabilities are different, so they can only be + // syntactically equivalent if they're both either kNonNullable or kLegacy. + // These are the two largest values of the enum, so we can just do a < check. + ASSERT(target::Nullability::kNullable < target::Nullability::kNonNullable && + target::Nullability::kNonNullable < target::Nullability::kLegacy); + __ Bind(&check_legacy); + __ CompareImmediate(A1, target::Nullability::kNonNullable); + __ BranchIf(LT, ¬_equal); + __ CompareImmediate(A0, target::Nullability::kNonNullable); + __ BranchIf(GE, &equal); + + __ Bind(¬_equal); + __ LoadObject(A0, CastHandle(FalseObject())); + __ ret(); + __ Bind(normal_ir_body); } @@ -1191,7 +1516,13 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler, void AsmIntrinsifier::FunctionType_equality(Assembler* assembler, Label* normal_ir_body) { - // TODO(riscv) + __ lx(A0, Address(SP, 1 * target::kWordSize)); + __ lx(A1, Address(SP, 0 * target::kWordSize)); + __ bne(A0, A1, normal_ir_body, Assembler::kNearJump); + + __ LoadObject(A0, CastHandle(TrueObject())); + __ ret(); + __ Bind(normal_ir_body); }