summaryrefslogtreecommitdiff
path: root/yjit/src/codegen.rs
AgeCommit message (Collapse)Author
2024-09-05Optimized instruction for Hash#freezeÉtienne Barrié
If a Hash which is empty or only using literals is frozen, we detect this as a peephole optimization and change the instructions to be `opt_hash_freeze`. [Feature #20684] Co-authored-by: Jean Boussier <byroot@ruby-lang.org> Notes: Merged: https://github.com/ruby/ruby/pull/11406
2024-09-05Optimized instruction for Array#freezeÉtienne Barrié
If an Array which is empty or only using literals is frozen, we detect this as a peephole optimization and change the instructions to be `opt_ary_freeze`. [Feature #20684] Co-authored-by: Jean Boussier <byroot@ruby-lang.org> Notes: Merged: https://github.com/ruby/ruby/pull/11406
2024-08-27YJIT: Pass method arguments using registers (#11280)Takashi Kokubun
* YJIT: Pass method arguments using registers * s/at_current_insn/at_compile_target/ * Implement register shuffle Notes: Merged-By: k0kubun <takashikkbn@gmail.com>
2024-08-08YJIT: Allow tracing fallback counters (#11347)Takashi Kokubun
* YJIT: Allow tracing fallback counters * Update yjit.md about --yjit-trace-exits=counter Notes: Merged-By: k0kubun <takashikkbn@gmail.com>
2024-08-02YJIT: Enhance the `String#<<` method substitution to handle integer ↵Kevin Menard
codepoint values. (#11032) * Document why we need to explicitly spill registers. * Simplify passing a byte value to `str_buf_cat`. * YJIT: Enhance the `String#<<` method substitution to handle integer codepoint values. * YJIT: Move runtime type check into YJIT. Performing the check in YJIT means we can make assumptions about the type. It also improves correctness of stack traces in cases where the codepoint argument is not a String or a Fixnum. Notes: Merged-By: maximecb <maximecb@ruby-lang.org>
2024-07-31YJIT: Decouple Context from encoding details (#11283)Takashi Kokubun
Notes: Merged-By: maximecb <maximecb@ruby-lang.org>
2024-07-29Expand opt_newarray_send to support Array#pack with buffer keyword argRandy Stauner
Use an enum for the method arg instead of needing to add an id that doesn't map to an actual method name. $ ruby --dump=insns -e 'b = "x"; [v].pack("E*", buffer: b)' before: ``` == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,34)> local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) [ 1] b@0 0000 putchilledstring "x" ( 1)[Li] 0002 setlocal_WC_0 b@0 0004 putself 0005 opt_send_without_block <calldata!mid:v, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0007 newarray 1 0009 putchilledstring "E*" 0011 getlocal_WC_0 b@0 0013 opt_send_without_block <calldata!mid:pack, argc:2, kw:[#<Symbol:0x000000000023110c>], KWARG> 0015 leave ``` after: ``` == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,34)> local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) [ 1] b@0 0000 putchilledstring "x" ( 1)[Li] 0002 setlocal_WC_0 b@0 0004 putself 0005 opt_send_without_block <calldata!mid:v, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0007 putchilledstring "E*" 0009 getlocal b@0, 0 0012 opt_newarray_send 3, 5 0015 leave ``` Notes: Merged: https://github.com/ruby/ruby/pull/11249
2024-07-18YJIT: Allow dev_nodebug to disasm release-mode code (#11198)Takashi Kokubun
* YJIT: Allow dev_nodebug to disasm release-mode code * Revert "YJIT: Squash canary before falling back" This reverts commit f05ad373d84909da7541bd6d6ace38b48eaf24a1. The stray canary issue should have been solved by def7023ee4a3fc6eeba9d3a34c31a5bcff315fac, alleviating this codegen accommodation. * s/runtime_assertions/runtime_checks/ --------- Co-authored-by: Alan Wu <XrXr@users.noreply.github.com> Notes: Merged-By: k0kubun <takashikkbn@gmail.com>
2024-07-15YJIT: Local variable register allocation (#11157)Takashi Kokubun
* YJIT: Local variable register allocation * locals are not stack temps * Rename RegTemps to RegMappings * Rename RegMapping to RegOpnd * Rename local_size to num_locals * s/stack value/operand/ * Rename spill_temps() to spill_regs() * Clarify when num_locals becomes None * Mention that InsnOut uses different registers * Rename get_reg_mapping to get_reg_opnd * Resurrect --yjit-temp-regs capability * Use MAX_CTX_TEMPS and MAX_CTX_LOCALS
2024-07-08YJIT: `dump-disasm`: Print comments and bytes in release buildsAlan Wu
This change implements a fallback mode for the `--yjit-dump-disasm` development command-line option to make it usable in release builds. Previously, using the option with release builds of YJIT yielded only a warning asking the user to build with `--enable-yjit=dev`. While builds that use the `disasm` feature still give the best output, just having the comments is useful enough for many kinds of debugging. Having it usable in release builds is nice for new hackers, too, since this allows for tinkering without having to learn how to build YJIT in development mode. Sample output on A64: ``` # regenerate_branch # Insn: 0001 opt_send_without_block (stack_size: 1) # guard known object with singleton class 0x11f7e0034: 4b 00 00 58 03 00 00 14 08 ce 9c 04 01 00 00 0x11f7e0043: 00 3f 00 0b eb 81 06 01 54 1f 20 03 d5 # RUBY_VM_CHECK_INTS(ec) 0x11f7e0050: 8b 02 42 b8 cb 07 01 35 # stack overflow check 0x11f7e0058: ab 62 02 91 7f 02 0b eb 69 07 01 54 # save PC to CFP 0x11f7e0064: 0b 3b 9a d2 2b 2f a0 f2 0b 00 cc f2 6b 02 00 0x11f7e0073: f8 ab 82 00 91 ``` To ensure this feature doesn't incur too much cost when running without the `--yjit-dump-disasm` option, I checked that there is no significant impact to compile time and memory usage with the `compile_time_ns` and `yjit_alloc_size` entry in `RubyVM::YJIT.runtime_stats`. For each sample, I ran 3 iterations of the `lobsters` YJIT benchmark. The statistics summary and done with the `summary` function in R. Compile time, sample size of 60, lower is better: ``` Before After Min. :2.054e+09 Min. :2.028e+09 1st Qu.:2.069e+09 1st Qu.:2.044e+09 Median :2.081e+09 Median :2.060e+09 Mean :2.089e+09 Mean :2.066e+09 3rd Qu.:2.109e+09 3rd Qu.:2.085e+09 Max. :2.146e+09 Max. :2.144e+09 ``` Allocation size, sample size of 20, lower is better: ``` Before After Min. :21804742 Min. :21794082 1st Qu.:21826682 1st Qu.:21816282 Median :21844042 Median :21826814 Mean :21960664 Mean :22026291 3rd Qu.:21861228 3rd Qu.:22040439 Max. :22587426 Max. :22930614 ``` The `yjit_alloc_size` samples are noisy, but since the average increased by only 0.3%, and the median is lower, I feel safe saying that there is no significant change.
2024-07-02YJIT: Inline simple ISEQs with unused keyword parametersGabriel Lacroix
This commit expands inlining for simple ISeqs to accept callees that have unused keyword parameters and callers that specify unused keywords. The following shows 2 new callsites that will be inlined: ```ruby def let(a, checked: true) = a let(1) let(1, checked: false) ``` Co-authored-by: Kaan Ozkan <kaan.ozkan@shopify.com>
2024-06-29[YJIT] Don't expand kwargs on forwardingAaron Patterson
Similarly to splat arrays, we shouldn't expand splat kwargs. [ruby-core:118401]
2024-06-28YJIT: Fix `cargo doc --document-private-items` warnings [ci skip]Alan Wu
Mostly putting angle brackets around links to follow markdown syntax.
2024-06-28YJIT: Move `ocb` parameters into `JITState`Alan Wu
Many functions take an outlined code block but do nothing more than passing it along; only a couple of functions actually make use of it. So, in most cases the `ocb` parameter is just boilerplate. Most functions that take `ocb` already also take a `JITState` and this commit moves `ocb` into `JITState` to remove the visual noise of the `ocb` parameter.
2024-06-26[YJIT] Fix block and splat handling when forwardingAaron Patterson
This commit fixes splat and block handling when calling in to a forwarding iseq. In the case of a splat we need to avoid expanding the array to the stack. We need to also ensure the CI write is flushed to the SP, otherwise it's possible for a block handler to clobber the CI [ruby-core:118360]
2024-06-18Add two new instructions for forwarding callsAaron Patterson
This commit adds `sendforward` and `invokesuperforward` for forwarding parameters to calls Co-authored-by: Matt Valentine-House <matt@eightbitraptor.com>
2024-06-18Optimized forwarding callers and calleesAaron Patterson
This patch optimizes forwarding callers and callees. It only optimizes methods that only take `...` as their parameter, and then pass `...` to other calls. Calls it optimizes look like this: ```ruby def bar(a) = a def foo(...) = bar(...) # optimized foo(123) ``` ```ruby def bar(a) = a def foo(...) = bar(1, 2, ...) # optimized foo(123) ``` ```ruby def bar(*a) = a def foo(...) list = [1, 2] bar(*list, ...) # optimized end foo(123) ``` All variants of the above but using `super` are also optimized, including a bare super like this: ```ruby def foo(...) super end ``` This patch eliminates intermediate allocations made when calling methods that accept `...`. We can observe allocation elimination like this: ```ruby def m x = GC.stat(:total_allocated_objects) yield GC.stat(:total_allocated_objects) - x end def bar(a) = a def foo(...) = bar(...) def test m { foo(123) } end test p test # allocates 1 object on master, but 0 objects with this patch ``` ```ruby def bar(a, b:) = a + b def foo(...) = bar(...) def test m { foo(1, b: 2) } end test p test # allocates 2 objects on master, but 0 objects with this patch ``` How does it work? ----------------- This patch works by using a dynamic stack size when passing forwarded parameters to callees. The caller's info object (known as the "CI") contains the stack size of the parameters, so we pass the CI object itself as a parameter to the callee. When forwarding parameters, the forwarding ISeq uses the caller's CI to determine how much stack to copy, then copies the caller's stack before calling the callee. The CI at the forwarded call site is adjusted using information from the caller's CI. I think this description is kind of confusing, so let's walk through an example with code. ```ruby def delegatee(a, b) = a + b def delegator(...) delegatee(...) # CI2 (FORWARDING) end def caller delegator(1, 2) # CI1 (argc: 2) end ``` Before we call the delegator method, the stack looks like this: ``` Executing Line | Code | Stack ---------------+---------------------------------------+-------- 1| def delegatee(a, b) = a + b | self 2| | 1 3| def delegator(...) | 2 4| # | 5| delegatee(...) # CI2 (FORWARDING) | 6| end | 7| | 8| def caller | -> 9| delegator(1, 2) # CI1 (argc: 2) | 10| end | ``` The ISeq for `delegator` is tagged as "forwardable", so when `caller` calls in to `delegator`, it writes `CI1` on to the stack as a local variable for the `delegator` method. The `delegator` method has a special local called `...` that holds the caller's CI object. Here is the ISeq disasm fo `delegator`: ``` == disasm: #<ISeq:delegator@-e:1 (1,0)-(1,39)> local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) [ 1] "..."@0 0000 putself ( 1)[LiCa] 0001 getlocal_WC_0 "..."@0 0003 send <calldata!mid:delegatee, argc:0, FCALL|FORWARDING>, nil 0006 leave [Re] ``` The local called `...` will contain the caller's CI: CI1. Here is the stack when we enter `delegator`: ``` Executing Line | Code | Stack ---------------+---------------------------------------+-------- 1| def delegatee(a, b) = a + b | self 2| | 1 3| def delegator(...) | 2 -> 4| # | CI1 (argc: 2) 5| delegatee(...) # CI2 (FORWARDING) | cref_or_me 6| end | specval 7| | type 8| def caller | 9| delegator(1, 2) # CI1 (argc: 2) | 10| end | ``` The CI at `delegatee` on line 5 is tagged as "FORWARDING", so it knows to memcopy the caller's stack before calling `delegatee`. In this case, it will memcopy self, 1, and 2 to the stack before calling `delegatee`. It knows how much memory to copy from the caller because `CI1` contains stack size information (argc: 2). Before executing the `send` instruction, we push `...` on the stack. The `send` instruction pops `...`, and because it is tagged with `FORWARDING`, it knows to memcopy (using the information in the CI it just popped): ``` == disasm: #<ISeq:delegator@-e:1 (1,0)-(1,39)> local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1]) [ 1] "..."@0 0000 putself ( 1)[LiCa] 0001 getlocal_WC_0 "..."@0 0003 send <calldata!mid:delegatee, argc:0, FCALL|FORWARDING>, nil 0006 leave [Re] ``` Instruction 001 puts the caller's CI on the stack. `send` is tagged with FORWARDING, so it reads the CI and _copies_ the callers stack to this stack: ``` Executing Line | Code | Stack ---------------+---------------------------------------+-------- 1| def delegatee(a, b) = a + b | self 2| | 1 3| def delegator(...) | 2 4| # | CI1 (argc: 2) -> 5| delegatee(...) # CI2 (FORWARDING) | cref_or_me 6| end | specval 7| | type 8| def caller | self 9| delegator(1, 2) # CI1 (argc: 2) | 1 10| end | 2 ``` The "FORWARDING" call site combines information from CI1 with CI2 in order to support passing other values in addition to the `...` value, as well as perfectly forward splat args, kwargs, etc. Since we're able to copy the stack from `caller` in to `delegator`'s stack, we can avoid allocating objects. I want to do this to eliminate object allocations for delegate methods. My long term goal is to implement `Class#new` in Ruby and it uses `...`. I was able to implement `Class#new` in Ruby [here](https://github.com/ruby/ruby/pull/9289). If we adopt the technique in this patch, then we can optimize allocating objects that take keyword parameters for `initialize`. For example, this code will allocate 2 objects: one for `SomeObject`, and one for the kwargs: ```ruby SomeObject.new(foo: 1) ``` If we combine this technique, plus implement `Class#new` in Ruby, then we can reduce allocations for this common operation. Co-Authored-By: John Hawthorn <john@hawthorn.email> Co-Authored-By: Alan Wu <XrXr@users.noreply.github.com>
2024-06-07YJIT: implement variable-length context encoding scheme (#10888)Maxime Chevalier-Boisvert
* Implement BitVector data structure for variable-length context encoding * Rename method to make intent clearer * Rename write_uint => push_uint to make intent clearer * Implement debug trait for BitVector * Fix bug in BitVector::read_uint_at(), enable more tests * Add one more test for good measure * Start sketching Context::encode() * Progress on variable length context encoding * Add tests. Fix bug. * Encode stack state * Add comments. Try to estimate context encoding size. * More compact encoding for stack size * Commit before rebase * Change Context::encode() to take a BitVector as input * Refactor BitVector::read_uint(), add helper read functions * Implement Context::decode() function. Add test. * Fix bug, add tests * Rename methods * Add Context::encode() and decode() methods using global data * Make encode and decode methods use u32 indices * Refactor YJIT to use variable-length context encoding * Tag functions as allow unused * Add a simple caching mechanism and stats for bytes per context etc * Add comments, fix formatting * Grow vector of bytes by 1.2x instead of 2x * Add debug assert to check round-trip encoding-decoding * Take some rustfmt formatting * Add decoded_from field to Context to reuse previous encodings * Remove olde context stats * Re-add stack_size assert * Disable decoded_from optimization for now
2024-06-04Do not emit shape transition warnings when YJIT is compilingJean Boussier
[Bug #20522] If `Warning.warn` is redefined in Ruby, emitting a warning would invoke Ruby code, which can't safely be done when YJIT is compiling.
2024-06-04YJIT: Fix getconstant exits after opt_ltlt fusion (#10903)Takashi Kokubun
Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
2024-05-31YJIT: Fix out of bounds access when splatting empty arrayAlan Wu
Previously, we read the last element array even when the array was empty, doing an out-of-bounds access. This sometimes caused a SEGV. [Bug #20496]
2024-05-28Stop marking chilled strings as frozenÉtienne Barrié
They were initially made frozen to avoid false positives for cases such as: str = str.dup if str.frozen? But this may cause bugs and is generally confusing for users. [Feature #20205] Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-05-23Introduce a specialize instruction for Array#packNobuyoshi Nakada
Instructions for this code: ```ruby # frozen_string_literal: true [a].pack("C") ``` Before this commit: ``` == disasm: #<ISeq:<main>@test.rb:1 (1,0)-(3,13)> 0000 putself ( 3)[Li] 0001 opt_send_without_block <calldata!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0003 newarray 1 0005 putobject "C" 0007 opt_send_without_block <calldata!mid:pack, argc:1, ARGS_SIMPLE> 0009 leave ``` After this commit: ``` == disasm: #<ISeq:<main>@test.rb:1 (1,0)-(3,13)> 0000 putself ( 3)[Li] 0001 opt_send_without_block <calldata!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0003 putobject "C" 0005 opt_newarray_send 2, :pack 0008 leave ``` Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com> Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
2024-05-01YJIT: Fix `Struct` accessors not firing tracing events (#10690)Alan Wu
* YJIT: Fix `Struct` accessors not firing tracing events Reading and writing to structs should fire `c_call` and `c_return`, but YJIT wasn't correctly dropping those calls when tracing. This has been missing since this functionality was added in 3081c83169c, but the added test only fails when ran in isolation with `--yjit-call-threshold=1`. The test sometimes failed on CI. * RJIT: YJIT: Fix `Struct` readers not firing tracing events Same issue as YJIT, but it looks like RJIT doesn't support writing to structs, so only reading needs changing.
2024-04-29YJIT: Expand codegen for `TrueClass#===` to `FalseClass` and `NilClass` (#10679)Randy Stauner
2024-04-29YJIT: Add specialized codegen function for `TrueClass#===` (#10640)Randy Stauner
* YJIT: Add specialized codegen function for `TrueClass#===` TrueClass#=== is currently number 10 in the most frequent C calls list of the lobsters benchmark. ``` require "benchmark/ips" def wrap true === true true === false true === :x end Benchmark.ips do |x| x.report(:wrap) do wrap end end ``` ``` before Warming up -------------------------------------- wrap 1.791M i/100ms Calculating ------------------------------------- wrap 17.806M (± 1.0%) i/s - 89.544M in 5.029363s after Warming up -------------------------------------- wrap 4.024M i/100ms Calculating ------------------------------------- wrap 40.149M (± 1.1%) i/s - 201.223M in 5.012527s ``` Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com> Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com> Co-authored-by: Kevin Menard <kevin.menard@shopify.com> Co-authored-by: Alan Wu <XrXr@users.noreply.github.com> * Fix the new test for RJIT --------- Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com> Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com> Co-authored-by: Kevin Menard <kevin.menard@shopify.com> Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
2024-04-26Revert "YJIT: Try splitting getlocal/setlocal blocks (#10648)"Takashi Kokubun
This reverts commit ab228bd0844758a1c444e39030c153874adf9120.
2024-04-26YJIT: Try splitting getlocal/setlocal blocks (#10648)Takashi Kokubun
2024-04-25YJIT: Relax `--yjit-verify-ctx` after singleton class creationAlan Wu
Types like `Type::CString` really only assert that at one point the object had its class field equal to `String`. Once a singleton class is created for any strings, the type makes no assertion about any class field anymore, and becomes the same as `Type::TString`. Previously, the `--yjit-verify-ctx` option wasn't allowing objects of these kind that have have singleton classes to pass verification even though the code generators handle it just fine. Found through `ruby/spec`.
2024-04-25YJIT: Optimize local variables when EP == BP (take 2) (#10607)Takashi Kokubun
* Revert "Revert "YJIT: Optimize local variables when EP == BP" (#10584)" This reverts commit c8783441952217c18e523749c821f82cd7e5d222. * YJIT: Take care of GC references in ISEQ invariants Co-authored-by: Alan Wu <alansi.xingwu@shopify.com> --------- Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
2024-04-24YJIT: Add a specialized codegen function for `Class#superclass`. (#10613)Kevin Menard
Add a specialized codegen function for `Class#superclass`. Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com> Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com> Co-authored-by: Randy Stauner <randy.stauner@shopify.com> Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
2024-04-19Revert "YJIT: Optimize local variables when EP == BP" (#10584)Alan Wu
This reverts commit 4cc58ea0b865f2fd20f1e881ddbd4c4fab0b072c. Since the change landed call-threshold=1 CI runs have been timing out. There has also been `verify-ctx` violations. Revert for now while we debug.
2024-04-18chore: remove repetitive words (#10573)careworry
Signed-off-by: careworry <worrycare@outlook.com>
2024-04-18YJIT: Fix canary crash with Array#<< (#10568)Alan Wu
Previously, we got "We are killing the stack canary set by opt_ltlt" from `$./miniruby --yjit-call-threshold=1 -e 'a = [].freeze; a << 1'` Found by running ruby-spec with yjit-call-threshold=1.
2024-04-17YJIT: Optimize local variables when EP == BP (#10487)Takashi Kokubun
2024-04-16YJIT: End send fallback blocks (#10539)Takashi Kokubun
2024-04-11YJIT: x64: Remove register shuffle with `opt_and` and friends (#10498)Alan Wu
This is best understood by looking at the change to the output: ```diff # Insn: 0002 opt_and (stack_size: 2) - mov rax, rsi - and rax, rdi - mov rsi, rax + and rsi, rdi ``` It's a bit awkward to match against due to how stack operands are lowered, but hey, it's nice to save the 2 unnecessary MOVs.
2024-04-03YJIT: Let sp_opnd take the number of slots (#10442)Takashi Kokubun
2024-03-28YJIT: Optimize putobject+opt_ltlt for integersAlan Wu
In `jit_rb_int_lshift()`, we guard against the right hand side changing since we want to avoid generating variable length shifts. When control reaches a `putobject` and `opt_ltlt` pair, though, we know that the right hand side never changes. This commit detects this situation and substitutes an implementation that does not guard against the right hand side changing, saving that work. Deleted some `putobject` Rust tests since they aren't that valuable and cause linking issues. Nice boost to `optcarrot` and `protoboeuf`: ``` ---------- ------------------ bench yjit-pre/yjit-post optcarrot 1.09 protoboeuf 1.12 ---------- ------------------ ```
2024-03-25YJIT: Inline simple getlocal+leave iseqsAlan Wu
This mainly targets things like `T.unsafe()` from Sorbet, which is just an identity function at runtime and only a hint for the static checker. Only deal with simple caller and callees (no keywords and splat etc.). Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>
2024-03-25YJIT: Propagate Array, Hash, and String classes (#10323)Takashi Kokubun
2024-03-20YJIT: Get rid of Type::TProc (#10287)Takashi Kokubun
2024-03-19Implement chilled stringsÉtienne Barrié
[Feature #20205] As a path toward enabling frozen string literals by default in the future, this commit introduce "chilled strings". From a user perspective chilled strings pretend to be frozen, but on the first attempt to mutate them, they lose their frozen status and emit a warning rather than to raise a `FrozenError`. Implementation wise, `rb_compile_option_struct.frozen_string_literal` is no longer a boolean but a tri-state of `enabled/disabled/unset`. When code is compiled with frozen string literals neither explictly enabled or disabled, string literals are compiled with a new `putchilledstring` instruction. This instruction is identical to `putstring` except it marks the String with the `STR_CHILLED (FL_USER3)` and `FL_FREEZE` flags. Chilled strings have the `FL_FREEZE` flag as to minimize the need to check for chilled strings across the codebase, and to improve compatibility with C extensions. Notes: - `String#freeze`: clears the chilled flag. - `String#-@`: acts as if the string was mutable. - `String#+@`: acts as if the string was mutable. - `String#clone`: copies the chilled flag. Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-03-18YJIT: Support arity=-2 cfuncs (#10268)Alan Wu
This type of cfuncs shows up as consume a lot of cycles in profiles of the lobsters benchmark, even though in the stats they don't happen that frequently. Might be a bug in the profiling, but these calls are not too bad to support, so might as well do it. Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
2024-03-13YJIT: Fallback cfunc varg splat for ruby2_keywords (#10226)Takashi Kokubun
2024-03-06YJIT: String#getbyte codegen (#10188)Maxime Chevalier-Boisvert
* WIP getbyte implementation * WIP String#getbyte implementation * Fix whitespace in stats.rs * fix? * Fix whitespace, add comment --------- Co-authored-by: Aaron Patterson <aaron.patterson@shopify.com>
2024-03-05[DOC] fix some commentscui fliter
Signed-off-by: cui fliter <imcusg@gmail.com>
2024-03-01Correctly set anon_kwrest flag for def f(b: 1, **)Jeremy Evans
In cases where a method accepts both keywords and an anonymous keyword splat, the method was not marked as taking an anonymous keyword splat. Fix that in the compiler. Doing that broke handling of nil keyword splats in yjit, so update yjit to handle that. Add a test to check that calling a method that accepts both a keyword argument and an anonymous keyword splat does not modify a passed keyword splat hash. Move the anon_kwrest check from setup_parameters_complex to ignore_keyword_hash_p, and only use it if the keyword hash is already a hash. This should speed things up slightly as it avoids a check previously used for all callers of setup_parameters_complex. Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-03-01YJIT: No need to set cfp->sp when setting escaped localsAlan Wu
While writing to the env object can add it to the remember set, it shouldn't trigger a GC run.
2024-02-29YJIT: Support inlining putself (#10137)Takashi Kokubun