| Age | Commit message (Collapse) | Author |
|
Notes:
Merged: https://github.com/ruby/ruby/pull/6306
|
|
Which restarts scanning the code range in unscanned part.
Notes:
Merged: https://github.com/ruby/ruby/pull/6306
|
|
|
|
|
|
|
|
`BufferedIO` is a bit inefficient for reading large responses because
it use the classic `buffer.slice!` technique which cause a lot of
unnecessary string copying.
This is particularly visible on line based protocol when reading
line by line.
Instead of repeatedly shifting the string, we can keep track of
which offset we're at, to know how many bytes are left in the buffer.
This change also open the door to further optimization by increasing
the buffer size, as previously `slice!` would get slower the larger
the buffer is.
Benchmark results:
```
=== 1k ===
Warming up --------------------------------------
1k 1.234k i/100ms
1k opt 1.283k i/100ms
Calculating -------------------------------------
1k 12.615k (± 0.9%) i/s - 64.168k in 5.086995s
1k opt 12.856k (± 0.9%) i/s - 65.433k in 5.090051s
Comparison:
1k: 12615.2 i/s
1k opt: 12856.0 i/s - 1.02x (± 0.00) faster
=== 10k ===
Warming up --------------------------------------
10k 1.165k i/100ms
10k opt 1.269k i/100ms
Calculating -------------------------------------
10k 11.550k (± 2.4%) i/s - 58.250k in 5.046378s
10k opt 12.736k (± 1.0%) i/s - 64.719k in 5.081969s
Comparison:
10k: 11550.3 i/s
10k opt: 12736.3 i/s - 1.10x (± 0.00) faster
=== 100k ===
Warming up --------------------------------------
100k 809.000 i/100ms
100k opt 926.000 i/100ms
Calculating -------------------------------------
100k 8.054k (± 3.0%) i/s - 40.450k in 5.028299s
100k opt 9.286k (± 2.2%) i/s - 47.226k in 5.088841s
Comparison:
100k: 8053.6 i/s
100k opt: 9285.5 i/s - 1.15x (± 0.00) faster
=== 1M ===
Warming up --------------------------------------
1M 249.000 i/100ms
1M opt 315.000 i/100ms
Calculating -------------------------------------
1M 2.448k (± 2.5%) i/s - 12.450k in 5.089744s
1M opt 3.119k (± 2.6%) i/s - 15.750k in 5.053772s
Comparison:
1M: 2447.8 i/s
1M opt: 3118.8 i/s - 1.27x (± 0.00) faster
```
Profiling before (1MB responses):
```
==================================
Mode: wall(1000)
Samples: 5276 (0.00% miss rate)
GC: 394 (7.47%)
==================================
TOTAL (pct) SAMPLES (pct) FRAME
1622 (30.7%) 1622 (30.7%) IO#wait_readable
777 (14.7%) 777 (14.7%) IO#read_nonblock
365 (6.9%) 365 (6.9%) (sweeping)
2705 (51.3%) 364 (6.9%) Net::BufferedIO#rbuf_fill
264 (5.0%) 264 (5.0%) String#index
223 (4.2%) 223 (4.2%) String#sub
221 (4.2%) 221 (4.2%) String#slice!
185 (3.5%) 185 (3.5%) String#split
108 (2.0%) 108 (2.0%) IO#write_nonblock
101 (1.9%) 101 (1.9%) String#downcase
66 (1.3%) 66 (1.3%) Net::BufferedIO#LOG
57 (1.1%) 57 (1.1%) String#count
51 (1.0%) 51 (1.0%) String#to_s
391 (7.4%) 50 (0.9%) Net::HTTPGenericRequest#write_header
50 (0.9%) 50 (0.9%) String#capitalize
49 (0.9%) 49 (0.9%) Array#join
47 (0.9%) 47 (0.9%) String#b
106 (2.0%) 36 (0.7%) Net::HTTPHeader#set_field
34 (0.6%) 34 (0.6%) Module#===
33 (0.6%) 33 (0.6%) String#[]
140 (2.7%) 29 (0.5%) Net::BufferedIO#write0
29 (0.5%) 29 (0.5%) (marking)
281 (5.3%) 27 (0.5%) Net::BufferedIO#rbuf_consume
1195 (22.6%) 25 (0.5%) Net::HTTPResponse#read_body
1024 (19.4%) 25 (0.5%) Net::HTTPResponse.each_response_header
86 (1.6%) 24 (0.5%) Net::HTTPHeader#set_field
23 (0.4%) 23 (0.4%) Net::HTTP#proxy_uri
51 (1.0%) 23 (0.4%) Net::HTTPHeader#initialize_http_header
2225 (42.2%) 22 (0.4%) Net::BufferedIO#readuntil
20 (0.4%) 20 (0.4%) Regexp#===
```
Profiling after (1MB responses):
```
==================================
Mode: wall(1000)
Samples: 15180 (0.00% miss rate)
GC: 1688 (11.12%)
==================================
TOTAL (pct) SAMPLES (pct) FRAME
4534 (29.9%) 4534 (29.9%) IO#read_nonblock
10650 (70.2%) 3944 (26.0%) Net::HTTPOpt::BufferedIOOpt#rbuf_fill
2101 (13.8%) 2101 (13.8%) IO#wait_readable
1442 (9.5%) 1442 (9.5%) (sweeping)
360 (2.4%) 360 (2.4%) String#sub
312 (2.1%) 312 (2.1%) String#split
265 (1.7%) 265 (1.7%) String#bytesize
246 (1.6%) 246 (1.6%) (marking)
151 (1.0%) 151 (1.0%) IO#write_nonblock
125 (0.8%) 125 (0.8%) String#downcase
116 (0.8%) 116 (0.8%) String#index
113 (0.7%) 113 (0.7%) Module#===
162 (1.1%) 89 (0.6%) Net::HTTPOpt::BufferedIOOpt#rbuf_consume_all_shareable!
158 (1.0%) 65 (0.4%) Net::HTTPHeader#set_field
63 (0.4%) 63 (0.4%) String#capitalize
63 (0.4%) 63 (0.4%) BasicObject#equal?
58 (0.4%) 58 (0.4%) Regexp#match
58 (0.4%) 58 (0.4%) String#[]
449 (3.0%) 56 (0.4%) Net::HTTPGenericRequest#write_header
53 (0.3%) 53 (0.3%) String#to_s
52 (0.3%) 52 (0.3%) Net::HTTPOpt::BufferedIOOpt#LOG
52 (0.3%) 52 (0.3%) String#count
44 (0.3%) 44 (0.3%) String#byteslice
44 (0.3%) 44 (0.3%) Array#join
1096 (7.2%) 42 (0.3%) Net::HTTPResponse.each_response_header
2617 (17.2%) 40 (0.3%) Net::HTTPOpt::BufferedIOOpt#readuntil
132 (0.9%) 30 (0.2%) Net::HTTPOpt::BufferedIOOpt#rbuf_consume
28 (0.2%) 28 (0.2%) Regexp#===
27 (0.2%) 27 (0.2%) Net::HTTP#proxy_uri
8862 (58.4%) 27 (0.2%) Net::HTTPResponse#read_body
````
Benchmark code:
```ruby
require "fileutils"
DIR = "/tmp/www"
FileUtils.mkdir_p(DIR)
HOST = "127.0.0.1"
PORT = 8080
CONF = <<~EOS
daemon off;
worker_processes 2;
events {
worker_connections 128;
}
http {
server_tokens off;
charset utf-8;
server {
server_name localhost;
listen #{HOST}:#{PORT};
keepalive_requests 10000000;
keepalive_timeout 3600s;
error_page 500 502 503 504 /50x.html;
location / {
root #{DIR};
}
}
}
EOS
File.write(File.join(DIR, "1k.txt"), 'a' * 1024)
File.write(File.join(DIR, "10k.txt"), 'a' * 1024 * 10)
File.write(File.join(DIR, "100k.txt"), 'a' * 1024 * 100)
File.write(File.join(DIR, "1M.txt"), 'a' * 1024 * 1024)
File.write(File.join(DIR, "nginx.conf"), CONF)
require "benchmark/ips"
require "net/http"
nginx_pid = Process.spawn('nginx', '-c', File.join(DIR, "nginx.conf"))
module Net
class HTTPOpt < HTTP
class BufferedIOOpt < ::Net::BufferedIO #:nodoc: internal use only
def initialize(io, read_timeout: 60, write_timeout: 60, continue_timeout: nil, debug_output: nil)
@io = io
@read_timeout = read_timeout
@write_timeout = write_timeout
@continue_timeout = continue_timeout
@debug_output = debug_output
@rbuf = ''.b
@rbuf_offset = 0
end
attr_reader :io
attr_accessor :read_timeout
attr_accessor :write_timeout
attr_accessor :continue_timeout
attr_accessor :debug_output
def inspect
"#<#{self.class} io=#{@io}>"
end
def eof?
@io.eof?
end
def closed?
@io.closed?
end
def close
@io.close
end
#
# Read
#
public
def read(len, dest = ''.b, ignore_eof = false)
LOG "reading #{len} bytes..."
read_bytes = 0
begin
while read_bytes + rbuf_size < len
if s = rbuf_consume_all_shareable!
read_bytes += s.bytesize
dest << s
end
rbuf_fill
end
s = rbuf_consume(len - read_bytes)
read_bytes += s.bytesize
dest << s
rescue EOFError
raise unless ignore_eof
end
LOG "read #{read_bytes} bytes"
dest
end
def read_all(dest = ''.b)
LOG 'reading all...'
read_bytes = 0
begin
while true
if s = rbuf_consume_all_shareable!
read_bytes += s.bytesize
dest << s
end
rbuf_fill
end
rescue EOFError
;
end
LOG "read #{read_bytes} bytes"
dest
end
def readuntil(terminator, ignore_eof = false)
offset = @rbuf_offset
begin
until idx = @rbuf.index(terminator, offset)
offset = @rbuf.bytesize
rbuf_fill
end
return rbuf_consume(idx + terminator.bytesize - @rbuf_offset)
rescue EOFError
raise unless ignore_eof
return rbuf_consume
end
end
def readline
readuntil("\n").chop
end
private
BUFSIZE = 1024 * 16
def rbuf_fill
tmp = @rbuf_empty ? @rbuf : nil
case rv = @io.read_nonblock(BUFSIZE, tmp, exception: false)
when String
@rbuf_empty = false
if rv.equal?(tmp)
@rbuf_offset = 0
else
@rbuf << rv
rv.clear
end
return
when :wait_readable
(io = @io.to_io).wait_readable(@read_timeout) or raise Net::ReadTimeout.new(io)
# continue looping
when :wait_writable
# OpenSSL::Buffering#read_nonblock may fail with IO::WaitWritable.
# http://www.openssl.org/support/faq.html#PROG10
(io = @io.to_io).wait_writable(@read_timeout) or raise Net::ReadTimeout.new(io)
# continue looping
when nil
raise EOFError, 'end of file reached'
end while true
end
def rbuf_flush
if @rbuf_empty
@rbuf.clear
@rbuf_offset = 0
end
nil
end
def rbuf_size
@rbuf.bytesize - @rbuf_offset
end
# Warning: this method may share the buffer to avoid
# copying. The caller must no longer use the returned
# string once rbuf_fill has been called again
def rbuf_consume_all_shareable!
@rbuf_empty = true
buf = if @rbuf_offset == 0
@rbuf
else
@rbuf.byteslice(@rbuf_offset..-1)
end
@rbuf_offset = @rbuf.bytesize
buf
end
def rbuf_consume(len = nil)
if @rbuf_offset == 0 && (len.nil? || len == @rbuf.bytesize)
s = @rbuf
@rbuf = ''.b
@rbuf_offset = 0
@rbuf_empty = true
elsif len.nil?
s = @rbuf.byteslice(@rbuf_offset..-1)
@rbuf = ''.b
@rbuf_offset = 0
@rbuf_empty = true
else
s = @rbuf.byteslice(@rbuf_offset, len)
@rbuf_offset += len
@rbuf_empty = @rbuf_offset == @rbuf.bytesize
rbuf_flush
end
@debug_output << %Q[-> #{s.dump}\n] if @debug_output
s
end
#
# Write
#
public
def write(*strs)
writing {
write0(*strs)
}
end
alias << write
def writeline(str)
writing {
write0 str + "\r\n"
}
end
private
def writing
@written_bytes = 0
@debug_output << '<- ' if @debug_output
yield
@debug_output << "\n" if @debug_output
bytes = @written_bytes
@written_bytes = nil
bytes
end
def write0(*strs)
@debug_output << strs.map(&:dump).join if @debug_output
orig_written_bytes = @written_bytes
strs.each_with_index do |str, i|
need_retry = true
case len = @io.write_nonblock(str, exception: false)
when Integer
@written_bytes += len
len -= str.bytesize
if len == 0
if strs.size == i+1
return @written_bytes - orig_written_bytes
else
need_retry = false
# next string
end
elsif len < 0
str = str.byteslice(len, -len)
else # len > 0
need_retry = false
# next string
end
# continue looping
when :wait_writable
(io = @io.to_io).wait_writable(@write_timeout) or raise Net::WriteTimeout.new(io)
# continue looping
end while need_retry
end
end
#
# Logging
#
private
def LOG_off
@save_debug_out = @debug_output
@debug_output = nil
end
def LOG_on
@debug_output = @save_debug_out
end
def LOG(msg)
return unless @debug_output
@debug_output << msg + "\n"
end
end
BufferedIO = BufferedIOOpt
# Unchanged from ruby 3.1.1, only allow to lookup the mofidied BufferedIO
def connect
if use_ssl?
# reference early to load OpenSSL before connecting,
# as OpenSSL may take time to load.
@ssl_context = OpenSSL::SSL::SSLContext.new
end
if proxy? then
conn_addr = proxy_address
conn_port = proxy_port
else
conn_addr = conn_address
conn_port = port
end
D "opening connection to #{conn_addr}:#{conn_port}..."
begin
s = Socket.tcp conn_addr, conn_port, @local_host, @local_port, connect_timeout: @open_timeout
rescue => e
e = Net::OpenTimeout.new(e) if e.is_a?(Errno::ETIMEDOUT) #for compatibility with previous versions
raise e, "Failed to open TCP connection to " +
"#{conn_addr}:#{conn_port} (#{e.message})"
end
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
D "opened"
if use_ssl?
if proxy?
plain_sock = BufferedIO.new(s, read_timeout: @read_timeout,
write_timeout: @write_timeout,
continue_timeout: @continue_timeout,
debug_output: @debug_output)
buf = "CONNECT #{conn_address}:#{@port} HTTP/#{HTTPVersion}\r\n"
buf << "Host: #{@address}:#{@port}\r\n"
if proxy_user
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m0')
buf << "Proxy-Authorization: Basic #{credential}\r\n"
end
buf << "\r\n"
plain_sock.write(buf)
HTTPResponse.read_new(plain_sock).value
# assuming nothing left in buffers after successful CONNECT response
end
ssl_parameters = Hash.new
iv_list = instance_variables
SSL_IVNAMES.each_with_index do |ivname, i|
if iv_list.include?(ivname)
value = instance_variable_get(ivname)
unless value.nil?
ssl_parameters[SSL_ATTRIBUTES[i]] = value
end
end
end
@ssl_context.set_params(ssl_parameters)
@ssl_context.session_cache_mode =
OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
@ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
D "starting SSL for #{conn_addr}:#{conn_port}..."
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
s.sync_close = true
# Server Name Indication (SNI) RFC 3546
s.hostname = @address if s.respond_to? :hostname=
if @ssl_session and
Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
s.session = @ssl_session
end
ssl_socket_connect(s, @open_timeout)
if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && @ssl_context.verify_hostname
s.post_connection_check(@address)
end
D "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}"
end
@socket = BufferedIO.new(s, read_timeout: @read_timeout,
write_timeout: @write_timeout,
continue_timeout: @continue_timeout,
debug_output: @debug_output)
@last_communicated = nil
on_connect
rescue => exception
if s
D "Conn close because of connect error #{exception}"
s.close
end
raise
end
private :connect
end
end
begin
sleep 0.2
connection = Net::HTTP.start(HOST, PORT)
connection.keep_alive_timeout = 3600
connection_opt = Net::HTTPOpt.start(HOST, PORT)
connection_opt.keep_alive_timeout = 3600
unless connection.request_get("/100k.txt").body == connection_opt.request_get("/100k.txt").body
abort("bug?")
end
if ARGV.first == "profile"
require 'stackprof'
require 'json'
StackProf.run(mode: :wall, out: "/tmp/stackprof-net-http.dump", raw: true) do
40_000.times do
connection.request_get("/1M.txt").body
end
end
File.write("/tmp/stackprof-net-http.json", JSON.dump(Marshal.load(File.binread("/tmp/stackprof-net-http.dump"))))
system("stackprof", "/tmp/stackprof-net-http.rb")
StackProf.run(mode: :wall, out: "/tmp/stackprof-net-http-opt.dump", raw: true) do
40_000.times do
connection_opt.request_get("/1M.txt").body
end
end
File.write("/tmp/stackprof-net-http-opt.json", JSON.dump(Marshal.load(File.binread("/tmp/stackprof-net-http-opt.dump"))))
system("stackprof", "/tmp/stackprof-net-http-opt.dump")
else
%w(1k 10k 100k 1M).each do |size|
puts "=== #{size} ==="
Benchmark.ips do |x|
path = "/#{size}.txt"
x.report("#{size}") { connection.request_get(path).body }
x.report("#{size} opt") { connection_opt.request_get(path).body }
x.compare!(order: :baseline)
end
puts
end
end
ensure
Process.kill('TERM', nginx_pid)
Process.wait(nginx_pid)
end
```
https://github.com/ruby/net-protocol/commit/781e400389
|
|
|
|
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/6304
|
|
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
Notes:
Merged: https://github.com/ruby/ruby/pull/6304
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/6304
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/6304
|
|
We're not fully in control of this folder, even when running our own
tests, because MJIT creates some temp folders there when invoking GC.
This bite tests running in ruby-core when making the behavior of
`FileUtils.rm_rf` more strict, because these extra files could not be
removed.
Since this was originally added due to some failures on systems with non
standard permissions on tmp folders, but I can no longer reproduce
those, I'll remove it.
https://github.com/rubygems/rubygems/commit/d2f21596ee
|
|
Fix up 27173e3735ff.
|
|
* Check only symbol flag bits
* Check all 4 bits
Notes:
Merged-By: maximecb <maximecb@ruby-lang.org>
|
|
I don't come up with a way to fix it right away. We'd need some
experiments on a pull request.
|
|
|
|
|
|
There are a lot of times when encoding AArch64 instructions that we
need to represent an integer value with a custom fixed width. For
example, the offset for a B instruction is 26 bits, so we store an
i32 on the instruction struct and then mask it when we encode.
We've been doing this masking everywhere, which has worked, but
it's getting a bit copy-pasty all over the place. This commit
centralizes that logic to make sure we stay consistent.
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
(https://github.com/Shopify/ruby/pull/442)
Previously we cleared the cache for all the code in the system when we
flip memory protection, which was prohibitively expensive since the
operation is not constant time. Instead, only clear the cache for the
memory region of newly written code when we write out new code.
This brings the runtime for the 30k_if_else test down to about 6 seconds
from the previous 45 seconds on my laptop.
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
(https://github.com/Shopify/ruby/pull/440)
for now
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
* Respect RUBY_TESTOPTS on test-all
* Increase the Cirrus timeout
* Increase the CSV test timeout
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
Add VMIL paper, update supported CPUs.
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
(https://github.com/Shopify/ruby/pull/430)
* Add --yjit-dump-disasm to dump every compiled code
* Just use get_option
* Carve out disasm_from_addr
* Avoid push_str with format!
* Share the logic through asm.compile
* This seems to negatively impact the compilation speed
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
* When we're storing an immediate 0 value at a memory address, we
can use STUR XZR, Xd instead of loading 0 into a register and
then storing that register.
* When we're moving 0 into an argument register, we can use
MOV Xd, XZR instead of loading the value into a register first.
* In the newarray instruction, we can skip looking at the stack at
all if the number of values we're using is 0.
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
(https://github.com/Shopify/ruby/pull/426)
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
(https://github.com/Shopify/ruby/pull/429)
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
(https://github.com/Shopify/ruby/pull/425)
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
(https://github.com/Shopify/ruby/pull/424)
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
(https://github.com/Shopify/ruby/pull/418)
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
* Remove references to explicit instruction parts
Previously we would reference individual instruction fields
manually. We can't do that with instructions that are enums, so
this commit removes those references. As a side effect, we can
remove the push_insn_parts() function from the assembler because we
now explicitly push instruction structs every time.
* Switch instructions to enum
Instructions are now no longer a large struct with a bunch of
optional fields. Instead they are an enum with individual shapes
for the variants.
In terms of size, the instruction struct was 120 bytes while the
new instruction enum is 106 bytes. The bigger win however is that
we're not allocating any vectors for instruction operands (except
for CCall), which should help cut down on memory usage.
Adding new instructions will be a little more complicated going
forward, but every mission-critical function that needs to be
touched will have an exhaustive match, so the compiler should guide
any additions.
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
This should fix a version string test
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
* Operand iterators
There are a couple of times when we're dealing with instructions
that we need to iterate through their operands. At the moment this
is relatively easy because there's an opnds field and we can work
with it directly. When the instructions become enums, however, the
shape of each variant will be different so we'll need an iterator
to make sense of the shape.
This commit introduces two new iterators that are created from an
instruction. One iterates over references to each operand (for
instances where they don't need to be mutable like updating live
ranges) and one iterates over mutable references to each operand
(for instances where you need to mutate them like loading values in
arm64).
Note that because iterators can't have generic items (i.e., be
associated with lifetimes) the mutable iterator forces you to use
the `while let Some` syntax as opposed to the for-loop like we did
with instructions.
This commit eliminates the last reference to insn.opnds, which is
going to make it much easier to transition to an enum.
* Consolidate output operand fetching
Currently we always look at the .out field on instructions whenever
we want to access the output operand. When the instructions become
an enum, this is not going to be possible since the shape of the
variants will be different. Instead, this commit introduces two
functions on Insn: out_opnd() and out_opnd_mut(). These return an
Option containing a reference to the output operand and a mutable
reference to the output operand, respectively.
This commit then uses those functions to replace all instances of
accessing the output operand. For the most part this was
straightforward; when we previously checked if it was Opnd::None
we now check that it's None, when we assumed there was an output
operand we now unwrap.
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
(https://github.com/Shopify/ruby/pull/420)
Yet another case of `jit_mov_gc_ptr()` being yanked out during the
transition to the new backend, causing a crash after object movement.
The intresting wrinkle with this one is that not all callinfos are GC'ed
objects, so the old code had an implicit assumption.
https://github.com/ruby/ruby/blob/b0b9f7201acab05c2a3ad92c3043a1f01df3e17f/yjit/src/codegen.rs#L4087-L4095
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
* Fix a bus error on regenerate_branch
* Fix pad_size
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
* Mutate in place for register allocation
Currently we allocate a new instruction every time when we're
doing register allocation by first splitting up the instruction
into its component parts, mapping the operands and the output, and
then pushing all of its parts onto the new assembler.
Since we don't need the old instruction, we can mutate the existing
one in place. While it's not that big of a win in and of itself, it
matches much more closely to what we're going to have to do when we
switch the instruction from being a struct to being an enum,
because it's much easier for the instruction to modify itself since
it knows its own shape than it is to push a new instruction that
very closely matches.
* Mutate in place for arm64 split
When we're splitting instructions for the arm64 backend, we map all
of the operands for a given instruction when it has an Opnd::Value.
We can do this in place with the existing operand instead of
allocating a new vector each time. This enables us to pattern match
against the entire instruction instead of just the opcode, which is
much closer to matching against an enum.
* Match against entire instruction in arm64_emit
Instead of matching against the opcode and then accessing all of
the various fields on the instruction when emitting bytecode for
arm64, we should instead match against the entire instruction.
This makes it much closer to what's going to happen when we switch
it over to being an enum.
* Match against entire instruction in x86_64 backend
When we're splitting or emitting code for x86_64, we should match
against the entire instruction instead of matching against just the
opcode. This gets us closer to matching against an enum instead of
a struct.
* Reuse instructions for arm64_split
When we're splitting, the default behavior was previously to split
up the instruction into its component parts and then reassemble
them in a new instruction. Instead, we can reuse the existing
instruction.
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
* Only check lowest bit for _Bool type
The `test AL, AL` got lost during porting and we were
generating `test RAX, RAX` instead. The upper bits of a `_Bool` return
type is unspecified and we were failing
`TestClass#test_singleton_class_should_has_own_namespace`
due to interpreterting the return value incorrectly.
* Enable test_class for test-all on x86_64
Notes:
Merged: https://github.com/ruby/ruby/pull/6289
|
|
When we're pushing instructions onto the assembler, we previously
would iterate through the instruction's operands and then assign
the output operand to it through the push_insn function. This is
easy when all instructions have a vector of operands, but is much
more difficult when the shape differs in an enum.
This commit changes it so that we explicitly define the output
operand for each instruction before it gets pushed onto the
assembler. This has the added benefit of changing the definition
of push_insn to no longer require a mutable instruction.
This paves the way to make the out field on the instructions an
Option<Opnd> instead which is going to more accurately reflect
the behavior we're going to have once we switch the instructions
over to an enum instead of a struct.
|
|
Currently we use macros to define the shape of each of the
instruction building methods. This works while all of the
instructions share the same fields, but is really hard to get
working when they're an enum with different shapes. This is an
incremental step toward a bigger refactor of changing the Insn
from a struct to an enum.
|
|
(https://github.com/Shopify/ruby/pull/409)
|
|
|
|
(https://github.com/Shopify/ruby/pull/407)
Make sure we can load the test-all runner and run test_yjit.rb
|
|
(https://github.com/Shopify/ruby/pull/402)
|
|
* Create code generation func
* Make rb_vm_concat_array available to use in Rust
* Map opcode to code gen func
* Implement code gen for concatarray
* Add test for concatarray
* Use new asm backend
* Add comment to C func wrapper
|