-
Notifications
You must be signed in to change notification settings - Fork 1.7k
cranelift: Implement table support for runtests #4430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| test run | ||
| target x86_64 | ||
| target s390x | ||
| target aarch64 | ||
|
|
||
| function %set_get_i64(i64 vmctx, i64, i64) -> i64 { | ||
| gv0 = vmctx | ||
| gv1 = load.i64 notrap aligned gv0 | ||
| gv2 = load.i64 notrap aligned gv0 +8 | ||
| table0 = dynamic gv1, element_size 8, bound gv2, index_type i64 | ||
|
|
||
| block0(v0: i64, v1: i64, v2: i64): | ||
| v3 = table_addr.i64 table0, v1, +0 | ||
| store.i64 v2, v3 | ||
| v4 = load.i64 v3 | ||
| return v4 | ||
| } | ||
| ; table: count=10, entry_size=8, ptr=vmctx+0, bound=vmctx+8 | ||
| ; run: %set_get_i64(0, 1) == 1 | ||
| ; run: %set_get_i64(0, 10) == 10 | ||
| ; run: %set_get_i64(1, 1) == 1 | ||
| ; run: %set_get_i64(1, 0xC0FFEEEE_DECAFFFF) == 0xC0FFEEEE_DECAFFFF | ||
| ; run: %set_get_i64(10, 1) == 1 | ||
| ; run: %set_get_i64(10, 0xC0FFEEEE_DECAFFFF) == 0xC0FFEEEE_DECAFFFF | ||
|
|
||
| function %set_get_i32(i64 vmctx, i64, i32) -> i32 { | ||
| gv0 = vmctx | ||
| gv1 = load.i64 notrap aligned gv0 | ||
| gv2 = load.i64 notrap aligned gv0 +8 | ||
| table0 = dynamic gv1, element_size 8, bound gv2, index_type i64 | ||
|
|
||
| block0(v0: i64, v1: i64, v2: i32): | ||
| ;; Note here the offset +4 | ||
| v3 = table_addr.i64 table0, v1, +4 | ||
| store.i32 v2, v3 | ||
| v4 = load.i32 v3 | ||
| return v4 | ||
| } | ||
| ; table: count=10, entry_size=8, ptr=vmctx+0, bound=vmctx+8 | ||
| ; run: %set_get_i32(0, 1) == 1 | ||
| ; run: %set_get_i32(0, 10) == 10 | ||
| ; run: %set_get_i32(1, 1) == 1 | ||
| ; run: %set_get_i32(1, 0xC0FFEEEE) == 0xC0FFEEEE | ||
| ; run: %set_get_i32(10, 1) == 1 | ||
| ; run: %set_get_i32(10, 0xC0FFEEEE) == 0xC0FFEEEE | ||
|
|
||
|
|
||
| function %set_get_i8(i64 vmctx, i64, i8) -> i8 { | ||
| gv0 = vmctx | ||
| gv1 = load.i64 notrap aligned gv0 | ||
| gv2 = load.i64 notrap aligned gv0 +8 | ||
| table0 = dynamic gv1, element_size 1, bound gv2, index_type i64 | ||
|
|
||
| block0(v0: i64, v1: i64, v2: i8): | ||
| v3 = table_addr.i64 table0, v1, +0 | ||
| store.i8 v2, v3 | ||
| v4 = load.i8 v3 | ||
| return v4 | ||
| } | ||
| ; table: count=2, entry_size=1, ptr=vmctx+0, bound=vmctx+8 | ||
| ; run: %set_get_i8(0, 1) == 1 | ||
| ; run: %set_get_i8(0, 0xC0) == 0xC0 | ||
| ; run: %set_get_i8(1, 1) == 1 | ||
| ; run: %set_get_i8(1, 0xFF) == 0xFF | ||
|
|
||
|
|
||
|
|
||
| function %large_elm_size(i64 vmctx, i64, i64, i8) -> i8 { | ||
| gv0 = vmctx | ||
| gv1 = load.i64 notrap aligned gv0 | ||
| gv2 = load.i64 notrap aligned gv0 +8 | ||
| table0 = dynamic gv1, element_size 10240, bound gv2, index_type i64 | ||
|
|
||
| block0(v0: i64, v1: i64, v2: i64, v3: i8): | ||
| v4 = table_addr.i64 table0, v1, +0 | ||
| v5 = iadd.i64 v4, v2 | ||
| store.i8 v3, v5 | ||
| v6 = load.i8 v5 | ||
| return v6 | ||
| } | ||
| ; table: count=5, entry_size=10240, ptr=vmctx+0, bound=vmctx+8 | ||
| ; run: %large_elm_size(0, 0, 1) == 1 | ||
| ; run: %large_elm_size(1, 0, 0xC0) == 0xC0 | ||
| ; run: %large_elm_size(0, 1, 1) == 1 | ||
| ; run: %large_elm_size(1, 1, 0xFF) == 0xFF | ||
| ; run: %large_elm_size(0, 127, 1) == 1 | ||
| ; run: %large_elm_size(1, 127, 0xFF) == 0xFF | ||
| ; run: %large_elm_size(0, 10239, 1) == 1 | ||
| ; run: %large_elm_size(1, 10239, 0xBB) == 0xBB | ||
|
|
||
|
|
||
| ; Tests writing a i64 which covers 8 table entries at once | ||
| ; Loads the first byte and the last to confirm that the slots were written | ||
| function %multi_elm_write(i64 vmctx, i64, i64) -> i8, i8 { | ||
| gv0 = vmctx | ||
| gv1 = load.i64 notrap aligned gv0 | ||
| gv2 = load.i64 notrap aligned gv0 +8 | ||
| table0 = dynamic gv1, element_size 1, bound gv2, index_type i64 | ||
|
|
||
| block0(v0: i64, v1: i64, v2: i64): | ||
| v3 = table_addr.i64 table0, v1, +0 | ||
| v4 = table_addr.i64 table0, v1, +7 | ||
| store.i64 v2, v3 | ||
| v5 = load.i8 v3 | ||
| v6 = load.i8 v4 | ||
| return v5, v6 | ||
| } | ||
| ; table: count=16, entry_size=1, ptr=vmctx+0, bound=vmctx+8 | ||
|
|
||
| ;; When writing these test cases keep in mind that s390x is big endian! | ||
| ;; We just make sure that the first and last byte are the same to deal with that. | ||
| ; run: %multi_elm_write(0, 0xC0FFEEEE_FFEEEEC0) == [0xC0, 0xC0] | ||
| ; run: %multi_elm_write(1, 0xAABBCCDD_EEFF00AA) == [0xAA, 0xAA] | ||
|
|
||
|
|
||
|
|
||
| ; Tests requesting multiple tables and heaps. This is mostly to test the runtest context | ||
| ; It also tests functions with a bunch of global values and tables and heaps | ||
| function %multi_tbl_heap(i64 vmctx, i64, i16) -> i16 { | ||
| gv0 = vmctx | ||
| gv1 = load.i64 notrap aligned gv0 +0 | ||
| gv2 = load.i64 notrap aligned gv0 +8 | ||
| gv3 = load.i64 notrap aligned gv0 +16 | ||
| gv4 = load.i64 notrap aligned gv0 +24 | ||
| gv5 = load.i64 notrap aligned gv0 +32 | ||
| gv6 = load.i64 notrap aligned gv0 +40 | ||
| gv7 = load.i64 notrap aligned gv0 +48 | ||
| gv8 = load.i64 notrap aligned gv0 +56 | ||
| gv9 = load.i64 notrap aligned gv0 +64 | ||
| gv10 = load.i64 notrap aligned gv0 +72 | ||
| gv11 = load.i64 notrap aligned gv0 +80 | ||
| gv12 = load.i64 notrap aligned gv0 +88 | ||
| gv13 = load.i64 notrap aligned gv0 +96 | ||
| gv14 = load.i64 notrap aligned gv0 +104 | ||
|
|
||
| table0 = dynamic gv1, element_size 1, bound gv2, index_type i64 | ||
| table1 = dynamic gv3, element_size 2, bound gv4, index_type i64 | ||
| heap0 = static gv5, min 0x10, bound 0x10, offset_guard 0, index_type i64 | ||
| heap1 = dynamic gv7, bound gv8, offset_guard 0, index_type i64 | ||
| table2 = dynamic gv9, element_size 3, bound gv10, index_type i64 | ||
| heap2 = dynamic gv11, bound gv12, offset_guard 0, index_type i64 | ||
| table3 = dynamic gv13, element_size 3, bound gv14, index_type i64 | ||
|
|
||
|
|
||
| block0(v0: i64, v1: i64, v2: i16): | ||
| v3 = table_addr.i64 table3, v1, +0; | ||
| store.i16 v2, v3 | ||
| v4 = load.i16 v3 | ||
| return v4 | ||
| } | ||
| ; table: count=1, entry_size=1, ptr=vmctx+0, bound=vmctx+8 | ||
| ; table: count=23, entry_size=2, ptr=vmctx+16, bound=vmctx+24 | ||
| ; heap: static, size=0x10, ptr=vmctx+32, bound=vmctx+40 | ||
| ; heap: dynamic, size=0x20, ptr=vmctx+48, bound=vmctx+56 | ||
| ; table: count=9, entry_size=3, ptr=vmctx+64, bound=vmctx+72 | ||
| ; heap: dynamic, size=0x3000, ptr=vmctx+80, bound=vmctx+88 | ||
| ; table: count=9, entry_size=3, ptr=vmctx+96, bound=vmctx+104 | ||
| ; run: %multi_tbl_heap(0, 1) == 1 | ||
| ; run: %multi_tbl_heap(0, 0xC0FF) == 0xC0FF | ||
| ; run: %multi_tbl_heap(1, 1) == 1 | ||
| ; run: %multi_tbl_heap(1, 0xC0FF) == 0xC0FF | ||
| ; run: %multi_tbl_heap(9, 1) == 1 | ||
| ; run: %multi_tbl_heap(9, 0xC0FF) == 0xC0FF |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,58 +1,86 @@ | ||||||
| use anyhow::anyhow; | ||||||
| use cranelift_codegen::ir::immediates::Uimm64; | ||||||
| use cranelift_codegen::ir::{ArgumentPurpose, Function}; | ||||||
| use cranelift_reader::parse_heap_command; | ||||||
| use cranelift_reader::{Comment, HeapCommand}; | ||||||
| use cranelift_reader::{parse_heap_command, parse_table_command}; | ||||||
| use cranelift_reader::{Comment, HeapCommand, TableCommand}; | ||||||
|
|
||||||
| #[derive(Debug, Clone)] | ||||||
| pub enum RuntestEntry { | ||||||
| Heap(HeapCommand), | ||||||
| Table(TableCommand), | ||||||
| } | ||||||
|
|
||||||
| impl RuntestEntry { | ||||||
| /// Tries to parse an entry from a comment, returning None if it isn't possible | ||||||
| pub fn parse_from_comment(comment: &Comment) -> anyhow::Result<Option<Self>> { | ||||||
| if let Some(heap_command) = parse_heap_command(comment.text)? { | ||||||
| return Ok(Some(RuntestEntry::Heap(heap_command))); | ||||||
| } | ||||||
| if let Some(table_command) = parse_table_command(comment.text)? { | ||||||
| return Ok(Some(RuntestEntry::Table(table_command))); | ||||||
| } | ||||||
| Ok(None) | ||||||
| } | ||||||
|
|
||||||
| pub fn ptr_offset(&self) -> Option<Uimm64> { | ||||||
| match self { | ||||||
| RuntestEntry::Heap(heap) => heap.ptr_offset, | ||||||
| RuntestEntry::Table(table) => table.ptr_offset, | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| pub fn bound_offset(&self) -> Option<Uimm64> { | ||||||
| match self { | ||||||
| RuntestEntry::Heap(heap) => heap.bound_offset, | ||||||
| RuntestEntry::Table(table) => table.bound_offset, | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Stores info about the expected environment for a test function. | ||||||
| #[derive(Debug, Clone)] | ||||||
| pub struct RuntestEnvironment { | ||||||
| pub heaps: Vec<HeapCommand>, | ||||||
| pub entries: Vec<RuntestEntry>, | ||||||
| } | ||||||
|
|
||||||
| impl RuntestEnvironment { | ||||||
| /// Parse the environment from a set of comments | ||||||
| pub fn parse(comments: &[Comment]) -> anyhow::Result<Self> { | ||||||
| let mut env = RuntestEnvironment { heaps: Vec::new() }; | ||||||
| let mut env = RuntestEnvironment { | ||||||
| entries: Vec::new(), | ||||||
| }; | ||||||
|
|
||||||
| // The order of the VMCtx memory is going to be dictated by the order of the comments | ||||||
| // we also enforce the correct vmctx offsets on the comments based on that. | ||||||
| for comment in comments.iter() { | ||||||
| if let Some(heap_command) = parse_heap_command(comment.text)? { | ||||||
| let heap_index = env.heaps.len() as u64; | ||||||
| let expected_ptr = heap_index * 16; | ||||||
| if Some(expected_ptr) != heap_command.ptr_offset.map(|p| p.into()) { | ||||||
| let entry = env.entries.len() as u64; | ||||||
| let expected_ptr = entry * 16; | ||||||
| let expected_bound = (entry * 16) + 8; | ||||||
|
|
||||||
| if let Some(entry) = RuntestEntry::parse_from_comment(comment)? { | ||||||
| if Some(expected_ptr) != entry.ptr_offset().map(|p| p.into()) { | ||||||
| return Err(anyhow!( | ||||||
| "Invalid ptr offset, expected vmctx+{}", | ||||||
| expected_ptr | ||||||
| )); | ||||||
| } | ||||||
|
|
||||||
| let expected_bound = (heap_index * 16) + 8; | ||||||
| if Some(expected_bound) != heap_command.bound_offset.map(|p| p.into()) { | ||||||
| if Some(expected_bound) != entry.bound_offset().map(|p| p.into()) { | ||||||
| return Err(anyhow!( | ||||||
| "Invalid bound offset, expected vmctx+{}", | ||||||
| expected_bound | ||||||
| )); | ||||||
| } | ||||||
|
|
||||||
| env.heaps.push(heap_command); | ||||||
| env.entries.push(entry); | ||||||
| }; | ||||||
| } | ||||||
|
|
||||||
| Ok(env) | ||||||
| } | ||||||
|
|
||||||
| pub fn is_active(&self) -> bool { | ||||||
| !self.heaps.is_empty() | ||||||
| } | ||||||
|
|
||||||
| /// Allocates memory for heaps | ||||||
| pub fn allocate_memory(&self) -> Vec<HeapMemory> { | ||||||
| self.heaps | ||||||
| .iter() | ||||||
| .map(|cmd| { | ||||||
| let size: u64 = cmd.size.into(); | ||||||
| vec![0u8; size as usize] | ||||||
| }) | ||||||
| .collect() | ||||||
| !self.entries.is_empty() | ||||||
| } | ||||||
|
|
||||||
| /// Validates the signature of a [Function] ensuring that if this environment is active, the | ||||||
|
|
@@ -75,6 +103,34 @@ impl RuntestEnvironment { | |||||
|
|
||||||
| Ok(()) | ||||||
| } | ||||||
|
|
||||||
| /// Allocates a struct to be injected into the test. | ||||||
| pub fn runtime_struct( | ||||||
| &self, | ||||||
| mut alloc_heap: impl FnMut(u64) -> u64, | ||||||
| mut alloc_table: impl FnMut(u64, u64) -> u64, | ||||||
| ) -> Vec<u64> { | ||||||
| let context_struct = self | ||||||
| .entries | ||||||
| .iter() | ||||||
| .flat_map(|entry| match entry { | ||||||
| RuntestEntry::Heap(heap) => { | ||||||
| let size: u64 = heap.size.into(); | ||||||
| [alloc_heap(size), size] | ||||||
| } | ||||||
| RuntestEntry::Table(table) => { | ||||||
| let entry_size: u64 = table.entry_size.into(); | ||||||
| let entry_count: u64 = table.entry_count.into(); | ||||||
| let bytes = entry_size * entry_count; | ||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is wrong according to Table bounds should be the number of elements and not the number of bytes. However nothing works when I change it to that. Probably something to investigate further. See: Lines 769 to 770 in 1d319c0
|
||||||
|
|
||||||
| [alloc_table(entry_size, entry_count), bytes] | ||||||
| } | ||||||
| }) | ||||||
| .collect(); | ||||||
|
|
||||||
| context_struct | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| pub(crate) type HeapMemory = Vec<u8>; | ||||||
| pub(crate) type TableMemory = Vec<u8>; | ||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a behaviour change from what we had before.
We used to put the start and end pointers. However it looks like that's not whats expected by the
boundmechanism for heaps.This changes it to pass the size in bytes of the heap.