summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/code_generator.cc52
-rw-r--r--compiler/optimizing/code_generator.h2
-rw-r--r--compiler/optimizing/code_generator_arm64.cc9
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc9
-rw-r--r--compiler/optimizing/code_generator_mips.cc9
-rw-r--r--compiler/optimizing/code_generator_mips64.cc9
-rw-r--r--compiler/optimizing/code_generator_x86.cc9
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc9
-rw-r--r--compiler/optimizing/instruction_simplifier.cc184
-rw-r--r--compiler/optimizing/nodes.h50
10 files changed, 342 insertions, 0 deletions
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 2bbb570c8d..3b5699bccd 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -64,6 +64,7 @@
#include "ssa_liveness_analysis.h"
#include "stack_map.h"
#include "stack_map_stream.h"
+#include "string_builder_append.h"
#include "thread-current-inl.h"
#include "utils/assembler.h"
@@ -599,6 +600,57 @@ void CodeGenerator::GenerateInvokeCustomCall(HInvokeCustom* invoke) {
InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr);
}
+void CodeGenerator::CreateStringBuilderAppendLocations(HStringBuilderAppend* instruction,
+ Location out) {
+ ArenaAllocator* allocator = GetGraph()->GetAllocator();
+ LocationSummary* locations =
+ new (allocator) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
+ locations->SetOut(out);
+ instruction->GetLocations()->SetInAt(instruction->FormatIndex(),
+ Location::ConstantLocation(instruction->GetFormat()));
+
+ uint32_t format = static_cast<uint32_t>(instruction->GetFormat()->GetValue());
+ uint32_t f = format;
+ PointerSize pointer_size = InstructionSetPointerSize(GetInstructionSet());
+ size_t stack_offset = static_cast<size_t>(pointer_size); // Start after the ArtMethod*.
+ for (size_t i = 0, num_args = instruction->GetNumberOfArguments(); i != num_args; ++i) {
+ StringBuilderAppend::Argument arg_type =
+ static_cast<StringBuilderAppend::Argument>(f & StringBuilderAppend::kArgMask);
+ switch (arg_type) {
+ case StringBuilderAppend::Argument::kStringBuilder:
+ case StringBuilderAppend::Argument::kString:
+ case StringBuilderAppend::Argument::kCharArray:
+ static_assert(sizeof(StackReference<mirror::Object>) == sizeof(uint32_t), "Size check.");
+ FALLTHROUGH_INTENDED;
+ case StringBuilderAppend::Argument::kBoolean:
+ case StringBuilderAppend::Argument::kChar:
+ case StringBuilderAppend::Argument::kInt:
+ case StringBuilderAppend::Argument::kFloat:
+ locations->SetInAt(i, Location::StackSlot(stack_offset));
+ break;
+ case StringBuilderAppend::Argument::kLong:
+ case StringBuilderAppend::Argument::kDouble:
+ stack_offset = RoundUp(stack_offset, sizeof(uint64_t));
+ locations->SetInAt(i, Location::DoubleStackSlot(stack_offset));
+ // Skip the low word, let the common code skip the high word.
+ stack_offset += sizeof(uint32_t);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected arg format: 0x" << std::hex
+ << (f & StringBuilderAppend::kArgMask) << " full format: 0x" << format;
+ UNREACHABLE();
+ }
+ f >>= StringBuilderAppend::kBitsPerArg;
+ stack_offset += sizeof(uint32_t);
+ }
+ DCHECK_EQ(f, 0u);
+
+ size_t param_size = stack_offset - static_cast<size_t>(pointer_size);
+ DCHECK_ALIGNED(param_size, kVRegSize);
+ size_t num_vregs = param_size / kVRegSize;
+ graph_->UpdateMaximumNumberOfOutVRegs(num_vregs);
+}
+
void CodeGenerator::CreateUnresolvedFieldLocationSummary(
HInstruction* field_access,
DataType::Type field_type,
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index f70ecb612d..357c4bb606 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -546,6 +546,8 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> {
void GenerateInvokeCustomCall(HInvokeCustom* invoke);
+ void CreateStringBuilderAppendLocations(HStringBuilderAppend* instruction, Location out);
+
void CreateUnresolvedFieldLocationSummary(
HInstruction* field_access,
DataType::Type field_type,
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 3086882678..28a7583845 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -5411,6 +5411,15 @@ void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruc
HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
+void LocationsBuilderARM64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ codegen_->CreateStringBuilderAppendLocations(instruction, LocationFrom(x0));
+}
+
+void InstructionCodeGeneratorARM64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ __ Mov(w0, instruction->GetFormat()->GetValue());
+ codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
+}
+
void LocationsBuilderARM64::VisitUnresolvedInstanceFieldGet(
HUnresolvedInstanceFieldGet* instruction) {
FieldAccessCallingConventionARM64 calling_convention;
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 6469c6964a..7adfe5d71d 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -5722,6 +5722,15 @@ void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instr
HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
+void LocationsBuilderARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ codegen_->CreateStringBuilderAppendLocations(instruction, LocationFrom(r0));
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ __ Mov(r0, instruction->GetFormat()->GetValue());
+ codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
+}
+
void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
HUnresolvedInstanceFieldGet* instruction) {
FieldAccessCallingConventionARMVIXL calling_convention;
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 72334afa40..b3bce78940 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -9529,6 +9529,15 @@ void InstructionCodeGeneratorMIPS::VisitStaticFieldSet(HStaticFieldSet* instruct
instruction->GetValueCanBeNull());
}
+void LocationsBuilderMIPS::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ codegen_->CreateStringBuilderAppendLocations(instruction, Location::RegisterLocation(V0));
+}
+
+void InstructionCodeGeneratorMIPS::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ __ LoadConst32(A0, instruction->GetFormat()->GetValue());
+ codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
+}
+
void LocationsBuilderMIPS::VisitUnresolvedInstanceFieldGet(
HUnresolvedInstanceFieldGet* instruction) {
FieldAccessCallingConventionMIPS calling_convention;
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 0d3cb3b8ca..0a3a3fa156 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -7148,6 +7148,15 @@ void InstructionCodeGeneratorMIPS64::VisitStaticFieldSet(HStaticFieldSet* instru
HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
+void LocationsBuilderMIPS64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ codegen_->CreateStringBuilderAppendLocations(instruction, Location::RegisterLocation(V0));
+}
+
+void InstructionCodeGeneratorMIPS64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ __ LoadConst32(A0, instruction->GetFormat()->GetValue());
+ codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
+}
+
void LocationsBuilderMIPS64::VisitUnresolvedInstanceFieldGet(
HUnresolvedInstanceFieldGet* instruction) {
FieldAccessCallingConventionMIPS64 calling_convention;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index ca1723bfd2..7a73569ea5 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -5510,6 +5510,15 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instr
HandleFieldGet(instruction, instruction->GetFieldInfo());
}
+void LocationsBuilderX86::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ codegen_->CreateStringBuilderAppendLocations(instruction, Location::RegisterLocation(EAX));
+}
+
+void InstructionCodeGeneratorX86::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ __ movl(EAX, Immediate(instruction->GetFormat()->GetValue()));
+ codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
+}
+
void LocationsBuilderX86::VisitUnresolvedInstanceFieldGet(
HUnresolvedInstanceFieldGet* instruction) {
FieldAccessCallingConventionX86 calling_convention;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 7c293b8605..ee2ef77850 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -4882,6 +4882,15 @@ void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instru
HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
+void LocationsBuilderX86_64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ codegen_->CreateStringBuilderAppendLocations(instruction, Location::RegisterLocation(RAX));
+}
+
+void InstructionCodeGeneratorX86_64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
+ __ movl(CpuRegister(RDI), Immediate(instruction->GetFormat()->GetValue()));
+ codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
+}
+
void LocationsBuilderX86_64::VisitUnresolvedInstanceFieldGet(
HUnresolvedInstanceFieldGet* instruction) {
FieldAccessCallingConventionX86_64 calling_convention;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index e829576479..9cc5cad55a 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -25,6 +25,7 @@
#include "mirror/class-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "sharpening.h"
+#include "string_builder_append.h"
namespace art {
@@ -2479,6 +2480,186 @@ static bool NoEscapeForStringBufferReference(HInstruction* reference, HInstructi
return false;
}
+static bool TryReplaceStringBuilderAppend(HInvoke* invoke) {
+ DCHECK_EQ(invoke->GetIntrinsic(), Intrinsics::kStringBuilderToString);
+ if (invoke->CanThrowIntoCatchBlock()) {
+ return false;
+ }
+
+ HBasicBlock* block = invoke->GetBlock();
+ HInstruction* sb = invoke->InputAt(0);
+
+ // We support only a new StringBuilder, otherwise we cannot ensure that
+ // the StringBuilder data does not need to be populated for other users.
+ if (!sb->IsNewInstance()) {
+ return false;
+ }
+
+ // For now, we support only single-block recognition.
+ // (Ternary operators feeding the append could be implemented.)
+ for (const HUseListNode<HInstruction*>& use : sb->GetUses()) {
+ if (use.GetUser()->GetBlock() != block) {
+ return false;
+ }
+ }
+
+ // Collect args and check for unexpected uses.
+ // We expect one call to a constructor with no arguments, one constructor fence (unless
+ // eliminated), some number of append calls and one call to StringBuilder.toString().
+ bool seen_constructor = false;
+ bool seen_constructor_fence = false;
+ bool seen_to_string = false;
+ uint32_t format = 0u;
+ uint32_t num_args = 0u;
+ HInstruction* args[StringBuilderAppend::kMaxArgs]; // Added in reverse order.
+ for (const HUseListNode<HInstruction*>& use : sb->GetUses()) {
+ // The append pattern uses the StringBuilder only as the first argument.
+ if (use.GetIndex() != 0u) {
+ return false;
+ }
+ // We see the uses in reverse order because they are inserted at the front
+ // of the singly-linked list, so the StringBuilder.append() must come first.
+ HInstruction* user = use.GetUser();
+ if (!seen_to_string) {
+ if (user->IsInvokeVirtual() &&
+ user->AsInvokeVirtual()->GetIntrinsic() == Intrinsics::kStringBuilderToString) {
+ seen_to_string = true;
+ continue;
+ } else {
+ return false;
+ }
+ }
+ // Then we should see the arguments.
+ if (user->IsInvokeVirtual()) {
+ HInvokeVirtual* as_invoke_virtual = user->AsInvokeVirtual();
+ DCHECK(!seen_constructor);
+ DCHECK(!seen_constructor_fence);
+ StringBuilderAppend::Argument arg;
+ switch (as_invoke_virtual->GetIntrinsic()) {
+ case Intrinsics::kStringBuilderAppendObject:
+ // TODO: Unimplemented, needs to call String.valueOf().
+ return false;
+ case Intrinsics::kStringBuilderAppendString:
+ arg = StringBuilderAppend::Argument::kString;
+ break;
+ case Intrinsics::kStringBuilderAppendCharArray:
+ // TODO: Unimplemented, StringBuilder.append(char[]) can throw NPE and we would
+ // not have the correct stack trace for it.
+ return false;
+ case Intrinsics::kStringBuilderAppendBoolean:
+ arg = StringBuilderAppend::Argument::kBoolean;
+ break;
+ case Intrinsics::kStringBuilderAppendChar:
+ arg = StringBuilderAppend::Argument::kChar;
+ break;
+ case Intrinsics::kStringBuilderAppendInt:
+ arg = StringBuilderAppend::Argument::kInt;
+ break;
+ case Intrinsics::kStringBuilderAppendLong:
+ arg = StringBuilderAppend::Argument::kLong;
+ break;
+ case Intrinsics::kStringBuilderAppendCharSequence: {
+ ReferenceTypeInfo rti = user->AsInvokeVirtual()->InputAt(1)->GetReferenceTypeInfo();
+ if (!rti.IsValid()) {
+ return false;
+ }
+ ScopedObjectAccess soa(Thread::Current());
+ Handle<mirror::Class> input_type = rti.GetTypeHandle();
+ DCHECK(input_type != nullptr);
+ if (input_type.Get() == GetClassRoot<mirror::String>()) {
+ arg = StringBuilderAppend::Argument::kString;
+ } else {
+ // TODO: Check and implement for StringBuilder. We could find the StringBuilder's
+ // internal char[] inconsistent with the length, or the string compression
+ // of the result could be compromised with a concurrent modification, and
+ // we would need to throw appropriate exceptions.
+ return false;
+ }
+ break;
+ }
+ case Intrinsics::kStringBuilderAppendFloat:
+ case Intrinsics::kStringBuilderAppendDouble:
+ // TODO: Unimplemented, needs to call FloatingDecimal.getBinaryToASCIIConverter().
+ return false;
+ default: {
+ return false;
+ }
+ }
+ // Uses of the append return value should have been replaced with the first input.
+ DCHECK(!as_invoke_virtual->HasUses());
+ DCHECK(!as_invoke_virtual->HasEnvironmentUses());
+ if (num_args == StringBuilderAppend::kMaxArgs) {
+ return false;
+ }
+ format = (format << StringBuilderAppend::kBitsPerArg) | static_cast<uint32_t>(arg);
+ args[num_args] = as_invoke_virtual->InputAt(1u);
+ ++num_args;
+ } else if (user->IsInvokeStaticOrDirect() &&
+ user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr &&
+ user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
+ user->AsInvokeStaticOrDirect()->GetNumberOfArguments() == 1u) {
+ // After arguments, we should see the constructor.
+ // We accept only the constructor with no extra arguments.
+ DCHECK(!seen_constructor);
+ DCHECK(!seen_constructor_fence);
+ seen_constructor = true;
+ } else if (user->IsConstructorFence()) {
+ // The last use we see is the constructor fence.
+ DCHECK(seen_constructor);
+ DCHECK(!seen_constructor_fence);
+ seen_constructor_fence = true;
+ } else {
+ return false;
+ }
+ }
+
+ if (num_args == 0u) {
+ return false;
+ }
+
+ // Check environment uses.
+ for (const HUseListNode<HEnvironment*>& use : sb->GetEnvUses()) {
+ HInstruction* holder = use.GetUser()->GetHolder();
+ if (holder->GetBlock() != block) {
+ return false;
+ }
+ // Accept only calls on the StringBuilder (which shall all be removed).
+ // TODO: Carve-out for const-string? Or rely on environment pruning (to be implemented)?
+ if (holder->InputCount() == 0 || holder->InputAt(0) != sb) {
+ return false;
+ }
+ }
+
+ // Create replacement instruction.
+ HIntConstant* fmt = block->GetGraph()->GetIntConstant(static_cast<int32_t>(format));
+ ArenaAllocator* allocator = block->GetGraph()->GetAllocator();
+ HStringBuilderAppend* append =
+ new (allocator) HStringBuilderAppend(fmt, num_args, allocator, invoke->GetDexPc());
+ append->SetReferenceTypeInfo(invoke->GetReferenceTypeInfo());
+ for (size_t i = 0; i != num_args; ++i) {
+ append->SetArgumentAt(i, args[num_args - 1u - i]);
+ }
+ block->InsertInstructionBefore(append, invoke);
+ invoke->ReplaceWith(append);
+ // Copy environment, except for the StringBuilder uses.
+ for (size_t i = 0, size = invoke->GetEnvironment()->Size(); i != size; ++i) {
+ if (invoke->GetEnvironment()->GetInstructionAt(i) == sb) {
+ invoke->GetEnvironment()->RemoveAsUserOfInput(i);
+ invoke->GetEnvironment()->SetRawEnvAt(i, nullptr);
+ }
+ }
+ append->CopyEnvironmentFrom(invoke->GetEnvironment());
+ // Remove the old instruction.
+ block->RemoveInstruction(invoke);
+ // Remove the StringBuilder's uses and StringBuilder.
+ while (sb->HasNonEnvironmentUses()) {
+ block->RemoveInstruction(sb->GetUses().front().GetUser());
+ }
+ DCHECK(!sb->HasEnvironmentUses());
+ block->RemoveInstruction(sb);
+ return true;
+}
+
// Certain allocation intrinsics are not removed by dead code elimination
// because of potentially throwing an OOM exception or other side effects.
// This method removes such intrinsics when special circumstances allow.
@@ -2493,6 +2674,9 @@ void InstructionSimplifierVisitor::SimplifyAllocationIntrinsic(HInvoke* invoke)
invoke->GetBlock()->RemoveInstruction(invoke);
RecordSimplification();
}
+ } else if (invoke->GetIntrinsic() == Intrinsics::kStringBuilderToString &&
+ TryReplaceStringBuilderAppend(invoke)) {
+ RecordSimplification();
}
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 8a8b371758..cb53ae3ada 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1438,6 +1438,7 @@ class HLoopInformationOutwardIterator : public ValueObject {
M(Shr, BinaryOperation) \
M(StaticFieldGet, Instruction) \
M(StaticFieldSet, Instruction) \
+ M(StringBuilderAppend, Instruction) \
M(UnresolvedInstanceFieldGet, Instruction) \
M(UnresolvedInstanceFieldSet, Instruction) \
M(UnresolvedStaticFieldGet, Instruction) \
@@ -6889,6 +6890,55 @@ class HStaticFieldSet final : public HExpression<2> {
const FieldInfo field_info_;
};
+class HStringBuilderAppend final : public HVariableInputSizeInstruction {
+ public:
+ HStringBuilderAppend(HIntConstant* format,
+ uint32_t number_of_arguments,
+ ArenaAllocator* allocator,
+ uint32_t dex_pc)
+ : HVariableInputSizeInstruction(
+ kStringBuilderAppend,
+ DataType::Type::kReference,
+ // The runtime call may read memory from inputs. It never writes outside
+ // of the newly allocated result object (or newly allocated helper objects).
+ SideEffects::AllReads().Union(SideEffects::CanTriggerGC()),
+ dex_pc,
+ allocator,
+ number_of_arguments + /* format */ 1u,
+ kArenaAllocInvokeInputs) {
+ DCHECK_GE(number_of_arguments, 1u); // There must be something to append.
+ SetRawInputAt(FormatIndex(), format);
+ }
+
+ void SetArgumentAt(size_t index, HInstruction* argument) {
+ DCHECK_LE(index, GetNumberOfArguments());
+ SetRawInputAt(index, argument);
+ }
+
+ // Return the number of arguments, excluding the format.
+ size_t GetNumberOfArguments() const {
+ DCHECK_GE(InputCount(), 1u);
+ return InputCount() - 1u;
+ }
+
+ size_t FormatIndex() const {
+ return GetNumberOfArguments();
+ }
+
+ HIntConstant* GetFormat() {
+ return InputAt(FormatIndex())->AsIntConstant();
+ }
+
+ bool NeedsEnvironment() const override { return true; }
+
+ bool CanThrow() const override { return true; }
+
+ DECLARE_INSTRUCTION(StringBuilderAppend);
+
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(StringBuilderAppend);
+};
+
class HUnresolvedInstanceFieldGet final : public HExpression<1> {
public:
HUnresolvedInstanceFieldGet(HInstruction* obj,