Skip to content

Commit

Permalink
Adding method register validation
Browse files Browse the repository at this point in the history
Summary:
Checking properties established after Register Allocation Pass.
1. Param registers are contiguous.
2. Range instruction src registers are contiguous
3. No overly large range instruction (taken from DexInstruction::set_range_size).
4. no overly large src and dest register. (smaller than dest/src reg limit, and less or equal to max param register)

Reviewed By: NTillmann

Differential Revision: D44969530

fbshipit-source-id: d26dbfee0244a6a091e8e60a1d97333671a76c40
  • Loading branch information
ssj933 authored and facebook-github-bot committed Dec 6, 2023
1 parent 4a4543f commit bf1ea9e
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 19 deletions.
155 changes: 155 additions & 0 deletions checkers/MethodRegisterChecker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include "MethodRegisterChecker.h"

#include "DexClass.h"
#include "DexOpcode.h"
#include "IRInstruction.h"
#include "IROpcode.h"
#include "Interference.h"
#include "ScopedCFG.h"
#include "Show.h"
#include "Walkers.h"

namespace redex_properties {

/**
* Return the end of param register frame, to check there is no other
* instructions using register bigger than param registers.
* Also check in this method that param registers are continuous and
* crash program if not.
*/
reg_t get_param_end(const char* property_name,
const cfg::ControlFlowGraph& cfg,
DexMethod* method) {
auto params = cfg.get_param_instructions();
if (params.empty()) {
return regalloc::max_unsigned_value(16);
}
auto param_ops = InstructionIterable(params);
auto it = param_ops.begin();
reg_t prev_reg = it->insn->dest();
reg_t spacing = it->insn->dest_is_wide() ? 2 : 1;
++it;
for (; it != param_ops.end(); ++it) {
// check that the param registers are contiguous
always_assert_log(
(prev_reg + spacing) == it->insn->dest(),
"[%s] Param registers are not contiguous for method %s:\n%s",
property_name,
SHOW(method),
SHOW(params));
spacing = it->insn->dest_is_wide() ? 2 : 1;
prev_reg = it->insn->dest();
}
return prev_reg + spacing - 1;
}

void MethodRegisterChecker::run_checker(DexStoresVector& stores,
ConfigFiles& /* conf */,
PassManager& /* mgr */,
bool established) {
if (!established) {
return;
}
const auto& scope = build_class_scope(stores);
walk::parallel::code(scope, [&](DexMethod* method, IRCode& code) {
cfg::ScopedCFG cfg(&code);
// 1. Load param's registers are at the end of register frames.
reg_t max_param_reg =
get_param_end(get_name(get_property()), code.cfg(), method);
auto ii = cfg::InstructionIterable(*cfg);
for (auto it = ii.begin(); it != ii.end(); ++it) {
// Checking several things for each method:
auto insn = it->insn;

// 2. dest register is below max param reg and register limit.
if (insn->has_dest()) {
always_assert_log(
insn->dest() <= max_param_reg,
"[%s] Instruction %s refers to a register (v%u) > param"
" registers (%u) in method %s\n",
get_name(get_property()),
SHOW(insn),
insn->dest(),
max_param_reg,
SHOW(method));
size_t max_dest_reg = regalloc::max_unsigned_value(
regalloc::interference::dest_bit_width(it));
always_assert_log(
insn->dest() <= max_dest_reg,
"[%s] Instruction %s refers to a register (v%u) > max dest"
" register (%zu) in method %s\n",
get_name(get_property()),
SHOW(insn),
insn->dest(),
max_dest_reg,
SHOW(method));
}
bool is_range = false;
if (opcode::has_range_form(insn->opcode())) {
insn->denormalize_registers();
is_range = needs_range_conversion(insn);
if (is_range) {
// 3. invoke-range's registers are continuous
always_assert_log(insn->has_contiguous_range_srcs_denormalized(),
"[%s] Instruction %s has non-contiguous srcs in "
"method %s.\n",
get_name(get_property()),
SHOW(insn),
SHOW(method));

// 4. No overly large range instructions.
auto size = insn->srcs_size();
// From DexInstruction::set_range_size;
always_assert_log(
dex_opcode::format(opcode::range_version(insn->opcode())) ==
FMT_f5rc ||
size == (size & 0xff),
"[%s] Range instruction %s takes too much src size in method "
"%s.\n",
get_name(get_property()),
SHOW(insn),
SHOW(method));
}
insn->normalize_registers();
}
// 5. All src registers are below max param reg and register limits.
for (size_t i = 0; i < insn->srcs_size(); ++i) {
always_assert_log(
insn->src(i) <= max_param_reg,
"[%s] Instruction %s refers to a register (v%u) > param"
" registers (%u) in method %s\n",
get_name(get_property()),
SHOW(insn),
insn->src(i),
max_param_reg,
SHOW(method));
if (!is_range) {
auto max_src_reg = regalloc::interference::max_value_for_src(
insn, i, insn->src_is_wide(i));
always_assert_log(
insn->src(i) <= max_src_reg,
"[%s] Instruction %s refers to a register (v%u) > max src"
" registers (%u) in method %s\n",
get_name(get_property()),
SHOW(insn),
insn->src(i),
max_src_reg,
SHOW(method));
}
}
}
});
}

} // namespace redex_properties

namespace {
static redex_properties::MethodRegisterChecker s_checker;
} // namespace
21 changes: 21 additions & 0 deletions checkers/MethodRegisterChecker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

#include "RedexPropertyChecker.h"

namespace redex_properties {

class MethodRegisterChecker : public PropertyChecker {
public:
MethodRegisterChecker() : PropertyChecker(names::MethodRegister) {}

void run_checker(DexStoresVector&, ConfigFiles&, PassManager&, bool) override;
};

} // namespace redex_properties
14 changes: 14 additions & 0 deletions libredex/IRInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,20 @@ class IRInstruction final {
return has_move_result() || has_move_result_pseudo();
}

bool has_contiguous_range_srcs_denormalized() const {
if (srcs_size() == 0) {
return true;
}
auto last = src(0);
for (size_t i = 1; i < srcs_size(); ++i) {
if (src(i) - last != 1) {
return false;
}
last = src(i);
}
return true;
}

/*
* Information about operands.
*/
Expand Down
19 changes: 1 addition & 18 deletions libredex/InstructionLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,30 +365,13 @@ void lower_fill_array_data(DexMethod*, IRCode* code, IRList::iterator* it_) {
it->replace_ir_with_dex(dex_insn);
}

/*
* Necessary condition for an instruction to be converted to /range form
*/
bool has_contiguous_srcs(const IRInstruction* insn) {
if (insn->srcs_size() == 0) {
return true;
}
auto last = insn->src(0);
for (size_t i = 1; i < insn->srcs_size(); ++i) {
if (insn->src(i) - last != 1) {
return false;
}
last = insn->src(i);
}
return true;
}

void lower_to_range_instruction(DexMethod* method,
IRCode* code,
IRList::iterator* it_) {
auto& it = *it_;
const auto* insn = it->insn;
always_assert_log(
has_contiguous_srcs(insn),
insn->has_contiguous_range_srcs_denormalized(),
"Instruction %s has non-contiguous srcs in method %s.\nContext:\n%s\n",
SHOW(insn),
SHOW(method),
Expand Down
1 change: 1 addition & 0 deletions libredex/RedexProperties.def
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ REDEX_PROPS(NoResolvablePureRefs, false, false, false,
REDEX_PROPS(NoSpuriousGetClassCalls, false, false, false, false)
REDEX_PROPS(NoUnreachableInstructions, false, true, true, false)
REDEX_PROPS(RenameClass, false, false, false, false)
REDEX_PROPS(MethodRegister, false, false, true, true)
// This is different from above because it is only a marker that signals interning happened,
// but the property is not checked. New spurious getClass calls may be produced, but were
// cannot have been input-code null-checks.
Expand Down
1 change: 0 additions & 1 deletion opt/dedup_blocks/DedupBlocksPass.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class DedupBlocksPass : public Pass {
{NoInitClassInstructions, Preserves},
{NoUnreachableInstructions, Preserves},
{NoResolvablePureRefs, Preserves},

{RenameClass, Preserves},
};
}
Expand Down
1 change: 1 addition & 0 deletions opt/regalloc/RegAlloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class RegAllocPass : public Pass {
{NoResolvablePureRefs, Preserves},
{NoUnreachableInstructions, Preserves},
{RenameClass, Preserves},
{MethodRegister, Establishes},
};
}

Expand Down

0 comments on commit bf1ea9e

Please sign in to comment.