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
1 change: 1 addition & 0 deletions insns.def
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ splatkw
(VALUE hash, VALUE block)
(VALUE obj, VALUE block)
// attr bool leaf = false; /* has rb_to_hash_type() */
// attr bool zjit_profile = true;
{
if (NIL_P(hash)) {
obj = Qnil;
Expand Down
638 changes: 319 additions & 319 deletions parse.y

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions prism/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ errors:
- ARGUMENT_FORWARDING_UNBOUND
- ARGUMENT_NO_FORWARDING_AMPERSAND
- ARGUMENT_NO_FORWARDING_ELLIPSES
- ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA
- ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK
- ARGUMENT_NO_FORWARDING_STAR
- ARGUMENT_NO_FORWARDING_STAR_STAR
- ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT
Expand Down
28 changes: 25 additions & 3 deletions prism/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -13957,6 +13957,7 @@ parse_parameters(
bool allows_forwarding_parameters,
bool accepts_blocks_in_defaults,
bool in_block,
pm_diagnostic_id_t diag_id_forwarding,
uint16_t depth
) {
pm_do_loop_stack_push(parser, false);
Expand Down Expand Up @@ -14018,7 +14019,7 @@ parse_parameters(
}
case PM_TOKEN_UDOT_DOT_DOT: {
if (!allows_forwarding_parameters) {
pm_parser_err_current(parser, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES);
pm_parser_err_current(parser, diag_id_forwarding);
}

bool succeeded = update_parameter_state(parser, &parser->current, &order);
Expand Down Expand Up @@ -14682,6 +14683,7 @@ parse_block_parameters(
false,
accepts_blocks_in_defaults,
true,
is_lambda_literal ? PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA : PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK,
(uint16_t) (depth + 1)
);
if (!is_lambda_literal) {
Expand Down Expand Up @@ -18904,7 +18906,17 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
} else {
// https://bugs.ruby-lang.org/issues/19107
bool allow_trailing_comma = parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1;
params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, true, allow_trailing_comma, true, true, false, (uint16_t) (depth + 1));
params = parse_parameters(
parser,
PM_BINDING_POWER_DEFINED,
true,
allow_trailing_comma,
true,
true,
false,
PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
(uint16_t) (depth + 1)
);
}

lex_state_set(parser, PM_LEX_STATE_BEG);
Expand All @@ -18927,7 +18939,17 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL);
}

params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, false, false, true, true, false, (uint16_t) (depth + 1));
params = parse_parameters(
parser,
PM_BINDING_POWER_DEFINED,
false,
false,
true,
true,
false,
PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
(uint16_t) (depth + 1)
);

// Reject `def * = 1` and similar. We have to specifically check
// for them because they create ambiguity with optional arguments.
Expand Down
2 changes: 2 additions & 0 deletions prism/templates/src/diagnostic.c.erb
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_ARGUMENT_FORWARDING_UNBOUND] = { "unexpected `...` in an non-parenthesized call", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND] = { "unexpected `&`; no anonymous block parameter", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES] = { "unexpected ... when the parent method is not forwarding", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA] = { "unexpected ... in lambda argument", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK] = { "unexpected ... in block argument", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_NO_FORWARDING_STAR] = { "unexpected `*`; no anonymous rest parameter", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR] = { "unexpected `**`; no anonymous keyword rest parameter", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT] = { "unexpected `*` splat argument after a `**` keyword splat argument", PM_ERROR_LEVEL_SYNTAX },
Expand Down
12 changes: 11 additions & 1 deletion test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
a {|...|}
^~~ unexpected ... when the parent method is not forwarding
^~~ unexpected ... in block argument

def foo(...)
a {|...|}
^~~ unexpected ... in block argument
end

def foo
a {|...|}
^~~ unexpected ... in block argument
end

Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
->(...) {}
^~~ unexpected ... when the parent method is not forwarding
^~~ unexpected ... in lambda argument

def foo(...)
->(...) {}
^~~ unexpected ... in lambda argument
end

def foo
->(...) {}
^~~ unexpected ... in lambda argument
end

61 changes: 31 additions & 30 deletions yjit/src/cruby_bindings.inc.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ def stats_string
:vm_write_locals_count,
:vm_write_stack_count,
:vm_write_to_parent_iseq_local_count,
:vm_read_from_parent_iseq_local_count,

:guard_type_count,
:guard_type_exit_ratio,
Expand Down
41 changes: 6 additions & 35 deletions zjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,6 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::GetIvar { self_val, id, ic, state: _ } => gen_getivar(jit, asm, opnd!(self_val), *id, *ic),
Insn::SetGlobal { id, val, state } => no_output!(gen_setglobal(jit, asm, *id, opnd!(val), &function.frame_state(*state))),
Insn::GetGlobal { id, state } => gen_getglobal(jit, asm, *id, &function.frame_state(*state)),
&Insn::GetLocal { ep_offset, level, use_sp, .. } => gen_getlocal(asm, ep_offset, level, use_sp),
&Insn::IsBlockParamModified { ep } => gen_is_block_param_modified(asm, opnd!(ep)),
&Insn::GetBlockParam { ep_offset, level, state } => gen_getblockparam(jit, asm, ep_offset, level, &function.frame_state(state)),
&Insn::SetLocal { val, ep_offset, level } => no_output!(gen_setlocal(asm, opnd!(val), function.type_of(val), ep_offset, level)),
Expand Down Expand Up @@ -600,8 +599,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
&Insn::ArrayExtend { left, right, state } => { no_output!(gen_array_extend(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state))) },
Insn::LoadPC => gen_load_pc(asm),
Insn::LoadEC => gen_load_ec(),
Insn::LoadSP => gen_load_sp(),
&Insn::GetEP { level } => gen_get_ep(asm, level),
Insn::GetLEP => gen_get_lep(jit, asm),
Insn::LoadSelf => gen_load_self(),
&Insn::LoadField { recv, id, offset, return_type } => gen_load_field(asm, opnd!(recv), id, offset, return_type),
&Insn::StoreField { recv, id, offset, val } => no_output!(gen_store_field(asm, opnd!(recv), id, offset, opnd!(val), function.type_of(val))),
Expand Down Expand Up @@ -630,18 +629,6 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
/// Gets the EP of the ISeq of the containing method, or "local level".
/// Equivalent of GET_LEP() macro.
fn gen_get_lep(jit: &JITState, asm: &mut Assembler) -> Opnd {
// Equivalent of get_lvar_level() in compile.c
fn get_lvar_level(mut iseq: IseqPtr) -> u32 {
let local_iseq = unsafe { rb_get_iseq_body_local_iseq(iseq) };
let mut level = 0;
while iseq != local_iseq {
iseq = unsafe { rb_get_iseq_body_parent_iseq(iseq) };
level += 1;
}

level
}

let level = get_lvar_level(jit.iseq);
gen_get_ep(asm, level)
}
Expand Down Expand Up @@ -722,26 +709,6 @@ fn gen_unbox_fixnum(asm: &mut Assembler, val: Opnd) -> Opnd {
asm.rshift(val, Opnd::UImm(1))
}

/// Get a local variable from a higher scope or the heap. `local_ep_offset` is in number of VALUEs.
/// We generate this instruction with level=0 only when the local variable is on the heap, so we
/// can't optimize the level=0 case using the SP register.
fn gen_getlocal(asm: &mut Assembler, local_ep_offset: u32, level: u32, use_sp: bool) -> lir::Opnd {
let local_ep_offset = i32::try_from(local_ep_offset).unwrap_or_else(|_| panic!("Could not convert local_ep_offset {local_ep_offset} to i32"));
if level > 0 {
gen_incr_counter(asm, Counter::vm_read_from_parent_iseq_local_count);
}
let local = if use_sp {
assert_eq!(level, 0, "use_sp optimization should be used only for level=0 locals");
let offset = -(SIZEOF_VALUE_I32 * (local_ep_offset + 1));
Opnd::mem(64, SP, offset)
} else {
let ep = gen_get_ep(asm, level);
let offset = -(SIZEOF_VALUE_I32 * local_ep_offset);
Opnd::mem(64, ep, offset)
};
asm.load(local)
}

/// Set a local variable from a higher scope or the heap. `local_ep_offset` is in number of VALUEs.
/// We generate this instruction with level=0 only when the local variable is on the heap, so we
/// can't optimize the level=0 case using the SP register.
Expand Down Expand Up @@ -1230,6 +1197,10 @@ fn gen_load_ec() -> Opnd {
EC
}

fn gen_load_sp() -> Opnd {
SP
}

fn gen_load_self() -> Opnd {
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF)
}
Expand Down Expand Up @@ -1461,7 +1432,7 @@ fn gen_send_iseq_direct(
// which optional keyword arguments need their defaults evaluated.
// We write this to the local table slot at bits_start so that:
// 1. The interpreter can read it via checkkeyword if we side-exit
// 2. The JIT entry can read it via GetLocal
// 2. The JIT entry can read it from the callee frame slot
if unsafe { rb_get_iseq_flags_has_kw(iseq) } {
let keyword = unsafe { rb_get_iseq_body_param_keyword(iseq) };
let bits_start = unsafe { (*keyword).bits_start } as usize;
Expand Down
12 changes: 12 additions & 0 deletions zjit/src/cruby.rs
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,18 @@ pub fn iseq_name(iseq: IseqPtr) -> String {
}
}

// Equivalent of get_lvar_level() in compile.c
pub fn get_lvar_level(mut iseq: IseqPtr) -> u32 {
let local_iseq = unsafe { rb_get_iseq_body_local_iseq(iseq) };
let mut level = 0;
while iseq != local_iseq {
iseq = unsafe { rb_get_iseq_body_parent_iseq(iseq) };
level += 1;
}

level
}

// Location is the file defining the method, colon, method name.
// Filenames are sometimes internal strings supplied to eval,
// so be careful with them.
Expand Down
61 changes: 31 additions & 30 deletions zjit/src/cruby_bindings.inc.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion zjit/src/cruby_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,10 @@ fn inline_kernel_block_given_p(fun: &mut hir::Function, block: hir::BlockId, _re

let local_iseq = unsafe { rb_get_iseq_body_local_iseq(fun.iseq()) };
if unsafe { rb_get_iseq_body_type(local_iseq) } == ISEQ_TYPE_METHOD {
let lep = fun.push_insn(block, hir::Insn::GetLEP);
// Get the EP of the ISeq of the containing method, or "local level", skipping over block-level EPs.
// Equivalent of GET_LEP() macro.
let level = crate::cruby::get_lvar_level(fun.iseq());
let lep = fun.push_insn(block, hir::Insn::GetEP { level });
Some(fun.push_insn(block, hir::Insn::IsBlockGiven { lep }))
} else {
Some(fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(Qfalse) }))
Expand Down
Loading