Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -4357,7 +4357,9 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
INSN *niobj = (INSN *)iobj->link.next;
if ((IS_INSN_ID(niobj, getlocal) ||
IS_INSN_ID(niobj, getinstancevariable) ||
IS_INSN_ID(niobj, putself)) &&
IS_INSN_ID(niobj, putself) ||
IS_INSN_ID(niobj, putobject) ||
IS_INSN_ID(niobj, putnil)) &&
IS_NEXT_INSN_ID(&niobj->link, send)) {

LINK_ELEMENT *sendobj = &(niobj->link); // Below we call ->next;
Expand Down
28 changes: 0 additions & 28 deletions include/ruby/internal/core/robject.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,32 +116,4 @@ struct RObject {
} as;
};

RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
* Queries the instance variables.
*
* @param[in] obj Object in question.
* @return Its instance variables, in C array.
* @pre `obj` must be an instance of ::RObject.
*
* @internal
*
* @shyouhei finds no reason for this to be visible from extension libraries.
*/
static inline VALUE *
ROBJECT_FIELDS(VALUE obj)
{
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);

struct RObject *const ptr = ROBJECT(obj);

if (RB_UNLIKELY(RB_FL_ANY_RAW(obj, ROBJECT_HEAP))) {
return ptr->as.heap.fields;
}
else {
return ptr->as.ary;
}
}

#endif /* RBIMPL_ROBJECT_H */
15 changes: 15 additions & 0 deletions internal/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,21 @@ RBASIC_SET_CLASS(VALUE obj, VALUE klass)
RB_OBJ_WRITTEN(obj, oldv, klass);
}

static inline VALUE *
ROBJECT_FIELDS(VALUE obj)
{
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);

struct RObject *const ptr = ROBJECT(obj);

if (RB_UNLIKELY(RB_FL_ANY_RAW(obj, ROBJECT_HEAP))) {
return ptr->as.heap.fields;
}
else {
return ptr->as.ary;
}
}

static inline size_t
rb_obj_embedded_size(uint32_t fields_count)
{
Expand Down
10 changes: 2 additions & 8 deletions pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,8 @@ static const char endstr[] = "sSiIlLqQjJ";
static int
is_bigendian(void)
{
static int init = 0;
static int endian_value;
const char *p;

if (init) return endian_value;
init = 1;
p = (char*)&init;
return endian_value = p[0]?0:1;
static const union {int i; char b[1];} endian_value = {1};
return !endian_value.b[0];
}
# define BIGENDIAN_P() (is_bigendian())
#elif defined(WORDS_BIGENDIAN)
Expand Down
68 changes: 51 additions & 17 deletions set.c
Original file line number Diff line number Diff line change
Expand Up @@ -723,14 +723,15 @@ set_i_join(int argc, VALUE *argv, VALUE set)

/*
* call-seq:
* add(obj) -> self
* add(object) -> self
*
* Adds the given object to the set and returns self. Use Set#merge to
* add many elements at once.
* Adds the given +object+ to +self+, returns +self+:
*
* Set[1, 2].add(3) #=> Set[1, 2, 3]
* Set[1, 2].add([3, 4]) #=> Set[1, 2, [3, 4]]
* Set[1, 2].add(2) #=> Set[1, 2]
* set = Set[0, 1, 2]
* set.add(%w[a b c]) # => Set[0, 1, 2, ["a", "b", "c"]]
* set.add(0) # => Set[0, 1, 2, ["a", "b", "c"]]
*
* Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
*/
static VALUE
set_i_add(VALUE set, VALUE item)
Expand Down Expand Up @@ -1340,13 +1341,18 @@ set_i_xor(VALUE set, VALUE other)

/*
* call-seq:
* set | enum -> new_set
* self | enumerable -> new_set
*
* Returns a new \Set object containing
* the {union}[https://en.wikipedia.org/wiki/Union_(set_theory)]
* of +self+ and the given +enumerable+;
* that is, containing the elements of both +self+ and +enumerable+.
*
* Returns a new set built by merging the set and the elements of the
* given enumerable object.
* set = Set[0, 1, 2]
* set | Set[2, 1, 'a'] # => Set[0, 1, 2, "a"]
* set | set # => Set[0, 1, 2]
*
* Set[1, 2, 3] | Set[2, 4, 5] #=> Set[1, 2, 3, 4, 5]
* Set[1, 5, 'z'] | (1..6) #=> Set[1, 5, "z", 2, 3, 4, 6]
* Related: see {Methods for Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations].
*/
static VALUE
set_i_union(VALUE set, VALUE other)
Expand Down Expand Up @@ -1802,11 +1808,32 @@ set_i_disjoint(VALUE set, VALUE other)

/*
* call-seq:
* set <=> other -> -1, 0, 1, or nil
* self <=> object -> -1, 0, 1, or nil
*
* Compares +self+ and +object+.
*
* If +object+ is another set, returns:
*
* - +-1+, if +self+ is a proper subset of +object+.
* - +0+, if +self+ and +object+ have the same elements.
* - +1+, if +self+ is a proper superset of +object+.
* - +nil+, if none of the above;
* that is, if +self+ and +object+ each have one or more elements
* not included in the other.
*
* Returns 0 if the set are equal, -1 / 1 if the set is a
* proper subset / superset of the given set, or nil if
* they both have unique elements.
* Examples:
*
* set = Set[0, 1, 2]
* set <=> Set[3, 2, 1, 0] # => -1
* set <=> Set[2, 1, 0] # => 0
* set <=> Set[1, 0] # => 1
* set <=> Set[1, 0, 3] # => nil
*
* Returns +nil+ if +object+ is not a set:
*
* set <=> [2, 1, 0] # => nil # Array, not Set.
*
* Related: see {Methods for Comparing}[rdoc-ref:Set@Methods+for+Comparing].
*/
static VALUE
set_i_compare(VALUE set, VALUE other)
Expand Down Expand Up @@ -1862,9 +1889,16 @@ set_recursive_eql(VALUE set, VALUE dt, int recur)

/*
* call-seq:
* set == other -> true or false
* self == object -> true or false
*
* Returns whether +object+ is a set, and has the same elements as +self+:
*
* set = Set[0, 1, 2]
* set == Set[1, 2, 0] # => true
* set == [1, 2, 3] # => false
* set == Set[1, 2, '3'] # => false
*
* Returns true if two sets are equal.
* Related: see {Methods for Comparing}[rdoc-ref:Set@Methods+for+Comparing].
*/
static VALUE
set_i_eq(VALUE set, VALUE other)
Expand Down
7 changes: 7 additions & 0 deletions test/ruby/test_optimization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,13 @@ def test_opt_duparray_send_include_p
'@c = :b; [:a, :b].include?(@c)',
'@c = "b"; %i[a b].include?(@c.to_sym)',
'[:a, :b].include?(self) == false',
"[:a, :b].include?(:a)",
"[true, false].include?(false)",
"[1, nil].include?(nil)",
"# frozen_string_literal: true\n['a', 'b'].include?('a')",
"# frozen_string_literal: true\n['a', 'b'].include?('c') == false",
"# frozen_string_literal: true\n['a', 'b'].include?(2) == false",
"# frozen_string_literal: true\n['a', 'b'].include?(/a/) == false",
].each do |code|
iseq = RubyVM::InstructionSequence.compile(code)
insn = iseq.disasm
Expand Down
5 changes: 5 additions & 0 deletions test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
#
# This set of tests can be run with:
# make test-all TESTS=test/ruby/test_zjit.rb
#
# Instead of adding new tests here, you should probably
# be adding tests that run under the Rust test harness,
# say, in `codegen_tests.rs`. It parallelizes better and
# allows for easy inspection of VM internal states.

require 'test/unit'
require 'envutil'
Expand Down
65 changes: 49 additions & 16 deletions zjit/src/backend/lir.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt;
use std::mem::take;
use std::rc::Rc;
use crate::bitset::BitSet;
use crate::codegen::{local_size_and_idx_to_ep_offset, perf_symbol_range_start, perf_symbol_range_end};
use crate::codegen::{local_size_and_idx_to_ep_offset, perf_symbol_range_start, perf_symbol_range_end, register_with_perf};
use crate::cruby::{IseqPtr, RUBY_OFFSET_CFP_ISEQ, RUBY_OFFSET_CFP_JIT_RETURN, RUBY_OFFSET_CFP_PC, RUBY_OFFSET_CFP_SP, SIZEOF_VALUE_I32, VALUE, ZJIT_STACK_MAP_SHIFT, ZJIT_STACK_MAP_VREG_TAG, vm_stack_canary, YarvInsnIdx, zjit_jit_frame};
use crate::hir::{Invariant, SideExitReason};
use crate::hir;
Expand Down Expand Up @@ -549,22 +550,18 @@ pub struct SideExit {
pub stack: Vec<Opnd>,
pub locals: Vec<Opnd>,
pub iseq: IseqPtr,
/// If set, the side exit will call the recompile function with these arguments
/// to profile the send and invalidate the ISEQ for recompilation.
/// If set, the side exit will profile the current instruction and invalidate
/// the compiled ISEQ for recompilation.
pub recompile: Option<SideExitRecompile>,
}

/// Arguments for the recompile callback on side exit.
/// Metadata for the recompile callback on side exit.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct SideExitRecompile {
/// The frame's own iseq, where the runtime profile is recorded at `insn_idx`.
/// For an exit out of inlined code this is the inlined callee.
pub frame_iseq: Opnd,
/// The compiled unit whose version must be invalidated to force a recompile. For inlined
/// methods, this will be the outer function it was inlined into.
pub compiled_iseq: Opnd,
pub insn_idx: u32,
pub strategy: hir::Recompile,
}

/// Branch target (something that we can jump to)
Expand Down Expand Up @@ -1697,6 +1694,43 @@ impl Assembler
}

pub fn linearize_instructions(&self) -> Vec<Insn> {
// Wrap instructions emitted by `push_insns` with PosMarkers and record
// the emitted byte range under `symbol_name` in the perf map.
fn push_insns_with_perf_symbol(
insns: &mut Vec<Insn>,
symbol_name: &str,
push_insns: impl FnOnce(&mut Vec<Insn>),
) {
// ISEQ perf symbols cover the whole compiled ISEQ, including this
// padding. HIR perf needs a separate symbol because the padding
// doesn't belong to any HIR instruction.
if get_option!(perf) != Some(PerfMap::HIR) {
push_insns(insns);
return;
}

let symbol_name = symbol_name.to_string();
let start = Rc::new(RefCell::new(None));
let current = start.clone();
insns.push(Insn::PosMarker(Rc::new(move |code_ptr, _| {
let mut current = current.borrow_mut();
assert!(current.is_none(), "perf symbol range already open");
*current = Some(code_ptr);
})));

push_insns(insns);

insns.push(Insn::PosMarker(Rc::new(move |end, cb| {
if let Some(start) = start.borrow_mut().take() {
let start_addr = start.raw_addr(cb);
let end_addr = end.raw_addr(cb);
if start_addr < end_addr {
register_with_perf(symbol_name.clone(), start_addr, end_addr - start_addr);
}
}
})));
}

// Emit instructions with labels, expanding branch parameters
let mut insns = Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY);

Expand All @@ -1708,7 +1742,9 @@ impl Assembler
// Entry blocks shouldn't ever be preceded by something that can
// stomp on this block.
if !block.is_entry {
insns.push(Insn::PadPatchPoint);
push_insns_with_perf_symbol(&mut insns, "PadPatchPoint", |insns| {
insns.push(Insn::PadPatchPoint);
});
}

// Process each instruction, expanding branch params if needed
Expand All @@ -1730,7 +1766,9 @@ impl Assembler

// Make sure we don't stomp on the next function
if block_id.0 == num_blocks - 1 {
insns.push(Insn::PadPatchPoint);
push_insns_with_perf_symbol(&mut insns, "PadPatchPoint", |insns| {
insns.push(Insn::PadPatchPoint);
});
}
}
insns
Expand Down Expand Up @@ -2588,15 +2626,10 @@ impl Assembler
let payload = get_or_create_iseq_payload(exit.iseq);
payload.reset_profiles_remaining(recompile.insn_idx as YarvInsnIdx);
use crate::codegen::exit_recompile;
let (profile_kind, profile_payload) = recompile.strategy.to_c_args();
asm_comment!(asm, "profile and maybe recompile");
asm_ccall!(asm, exit_recompile,
EC,
recompile.frame_iseq,
recompile.compiled_iseq,
recompile.insn_idx.into(),
profile_kind.into(),
profile_payload.into()
recompile.compiled_iseq
);
}
}
Expand Down
Loading