// Walk over all uses covered by this interval, and update the location
// information.
- while (use != nullptr && use->GetPosition() <= current->GetEnd()) {
- LocationSummary* locations = use->GetUser()->GetLocations();
- if (use->GetIsEnvironment()) {
- locations->SetEnvironmentAt(use->GetInputIndex(), source);
- } else {
- Location expected_location = locations->InAt(use->GetInputIndex());
- // The expected (actual) location may be invalid in case the input is unused. Currently
- // this only happens for intrinsics.
- if (expected_location.IsValid()) {
- if (expected_location.IsUnallocated()) {
- locations->SetInAt(use->GetInputIndex(), source);
- } else if (!expected_location.IsConstant()) {
- AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location);
- }
+
+ LiveRange* range = current->GetFirstRange();
+ while (range != nullptr) {
+ while (use != nullptr && use->GetPosition() < range->GetStart()) {
+ DCHECK(use->GetIsEnvironment());
+ use = use->GetNext();
+ }
+ while (use != nullptr && use->GetPosition() <= range->GetEnd()) {
+ DCHECK(current->Covers(use->GetPosition()) || (use->GetPosition() == range->GetEnd()));
+ LocationSummary* locations = use->GetUser()->GetLocations();
+ if (use->GetIsEnvironment()) {
+ locations->SetEnvironmentAt(use->GetInputIndex(), source);
} else {
- DCHECK(use->GetUser()->IsInvoke());
- DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone);
+ Location expected_location = locations->InAt(use->GetInputIndex());
+ // The expected (actual) location may be invalid in case the input is unused. Currently
+ // this only happens for intrinsics.
+ if (expected_location.IsValid()) {
+ if (expected_location.IsUnallocated()) {
+ locations->SetInAt(use->GetInputIndex(), source);
+ } else if (!expected_location.IsConstant()) {
+ AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location);
+ }
+ } else {
+ DCHECK(use->GetUser()->IsInvoke());
+ DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone);
+ }
}
+ use = use->GetNext();
}
- use = use->GetNext();
+ range = range->GetNext();
}
// If the next interval starts just after this one, and has a register,
}
current = next_sibling;
} while (current != nullptr);
- DCHECK(use == nullptr);
+
+ if (kIsDebugBuild) {
+ // Following uses can only be environment uses. The location for
+ // these environments will be none.
+ while (use != nullptr) {
+ DCHECK(use->GetIsEnvironment());
+ use = use->GetNext();
+ }
+ }
}
void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval,
current->GetLiveInterval()->SetFrom(current->GetLifetimePosition());
}
- // All inputs of an instruction must be live.
- for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
- HInstruction* input = current->InputAt(i);
- // Some instructions 'inline' their inputs, that is they do not need
- // to be materialized.
- if (input->HasSsaIndex()) {
- live_in->SetBit(input->GetSsaIndex());
- input->GetLiveInterval()->AddUse(current, i, false);
- }
- }
-
+ // Process the environment first, because we know their uses come after
+ // or at the same liveness position of inputs.
if (current->HasEnvironment()) {
// Handle environment uses. See statements (b) and (c) of the
// SsaLivenessAnalysis.
HEnvironment* environment = current->GetEnvironment();
for (size_t i = 0, e = environment->Size(); i < e; ++i) {
HInstruction* instruction = environment->GetInstructionAt(i);
- if (ShouldBeLiveForEnvironment(instruction)) {
+ bool should_be_live = ShouldBeLiveForEnvironment(instruction);
+ if (should_be_live) {
DCHECK(instruction->HasSsaIndex());
live_in->SetBit(instruction->GetSsaIndex());
- instruction->GetLiveInterval()->AddUse(current, i, true);
}
+ if (instruction != nullptr) {
+ instruction->GetLiveInterval()->AddUse(
+ current, i, /* is_environment */ true, should_be_live);
+ }
+ }
+ }
+
+ // All inputs of an instruction must be live.
+ for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
+ HInstruction* input = current->InputAt(i);
+ // Some instructions 'inline' their inputs, that is they do not need
+ // to be materialized.
+ if (input->HasSsaIndex()) {
+ live_in->SetBit(input->GetSsaIndex());
+ input->GetLiveInterval()->AddUse(current, i, /* is_environment */ false);
}
}
}
AddRange(position, position + 1);
}
- void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) {
+ void AddUse(HInstruction* instruction,
+ size_t input_index,
+ bool is_environment,
+ bool keep_alive = false) {
// Set the use within the instruction.
size_t position = instruction->GetLifetimePosition() + 1;
LocationSummary* locations = instruction->GetLocations();
&& (first_use_->GetPosition() < position)) {
// The user uses the instruction multiple times, and one use dies before the other.
// We update the use list so that the latter is first.
+ DCHECK(!is_environment);
UsePosition* cursor = first_use_;
while ((cursor->GetNext() != nullptr) && (cursor->GetNext()->GetPosition() < position)) {
cursor = cursor->GetNext();
return;
}
+ first_use_ = new (allocator_) UsePosition(
+ instruction, input_index, is_environment, position, first_use_);
+
+ if (is_environment && !keep_alive) {
+ // If this environment use does not keep the instruction live, it does not
+ // affect the live range of that instruction.
+ return;
+ }
+
size_t start_block_position = instruction->GetBlock()->GetLifetimeStart();
if (first_range_ == nullptr) {
// First time we see a use of that interval.
// and the check line 205 would succeed.
first_range_ = new (allocator_) LiveRange(start_block_position, position, first_range_);
}
- first_use_ = new (allocator_) UsePosition(
- instruction, input_index, is_environment, position, first_use_);
}
void AddPhiUse(HInstruction* instruction, size_t input_index, HBasicBlock* block) {
UsePosition* use = first_use_;
size_t end = GetEnd();
while (use != nullptr && use->GetPosition() <= end) {
- size_t use_position = use->GetPosition();
- if (use_position > position) {
- return use_position;
+ if (!use->GetIsEnvironment()) {
+ size_t use_position = use->GetPosition();
+ if (use_position > position) {
+ return use_position;
+ }
}
use = use->GetNext();
}
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "arch/context.h"
+#include "jni.h"
+#include "mirror/art_method-inl.h"
+#include "scoped_thread_state_change.h"
+#include "stack.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+class TestVisitor : public StackVisitor {
+ public:
+ TestVisitor(Thread* thread, Context* context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : StackVisitor(thread, context) {}
+
+ bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* m = GetMethod();
+ std::string m_name(m->GetName());
+
+ if (m_name.compare("testLiveArgument") == 0) {
+ found_method_ = true;
+ uint32_t value = 0;
+ CHECK(GetVReg(m, 0, kIntVReg, &value));
+ CHECK_EQ(value, 42u);
+ } else if (m_name.compare("testIntervalHole") == 0) {
+ found_method_ = true;
+ uint32_t value = 0;
+ if (m->IsOptimized(sizeof(void*))) {
+ CHECK_EQ(GetVReg(m, 0, kIntVReg, &value), false);
+ } else {
+ CHECK(GetVReg(m, 0, kIntVReg, &value));
+ CHECK_EQ(value, 1u);
+ }
+ }
+
+ return true;
+ }
+
+ // Value returned to Java to ensure the methods testSimpleVReg and testPairVReg
+ // have been found and tested.
+ bool found_method_ = false;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doStaticNativeCallLiveVreg(JNIEnv*, jclass) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<Context> context(Context::Create());
+ TestVisitor visitor(soa.Self(), context.get());
+ visitor.WalkStack();
+ CHECK(visitor.found_method_);
+}
+
+} // namespace
+
+} // namespace art
--- /dev/null
+Tests for inspecting live DEX registers. The test must
+also pass for non-debuggable, as we need to know live DEX registers
+when de-opting.
--- /dev/null
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public Main() {
+ }
+
+ static int testLiveArgument(int arg) {
+ doStaticNativeCallLiveVreg();
+ return arg;
+ }
+
+ static void moveArgToCalleeSave() {
+ try {
+ Thread.sleep(0);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ static void testIntervalHole(int arg, boolean test) {
+ // Move the argument to callee save to ensure it is in
+ // a readable register.
+ moveArgToCalleeSave();
+ if (test) {
+ staticField1 = arg;
+ // The environment use of `arg` should not make it live.
+ doStaticNativeCallLiveVreg();
+ } else {
+ staticField2 = arg;
+ // The environment use of `arg` should not make it live.
+ doStaticNativeCallLiveVreg();
+ }
+ }
+
+ static native void doStaticNativeCallLiveVreg();
+
+ static {
+ System.loadLibrary("arttest");
+ }
+
+ public static void main(String[] args) {
+ if (testLiveArgument(42) != 42) {
+ throw new Error("Expected 42");
+ }
+
+ if (testLiveArgument(42) != 42) {
+ throw new Error("Expected 42");
+ }
+
+ testIntervalHole(1, true);
+ testIntervalHole(1, false);
+ }
+
+ static int staticField1;
+ static int staticField2;
+}
454-get-vreg/get_vreg_jni.cc \
455-set-vreg/set_vreg_jni.cc \
457-regs/regs_jni.cc \
- 461-get-reference-vreg/get_reference_vreg_jni.cc
+ 461-get-reference-vreg/get_reference_vreg_jni.cc \
+ 466-get-live-vreg/get_live_vreg_jni.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ifdef TARGET_2ND_ARCH
455-set-vreg \
457-regs \
461-get-reference-vreg \
+ 466-get-live-vreg \
ifneq (,$(filter ndebug,$(RUN_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \