I'm quite new. Wondering what's wrong here:
function u0:0() system_v {
ss0 = explicit_slot 4
block0:
v0 = stack_addr.i32 ss0
v1 = iconst.i32 1
store v0, v1 ; v1 = 1
v2 = iconst.i32 0
return
}
looks like I want stack_store
instead
On 64bit systems you need to use stack_addr.i64
instead. The type param of stack_addr
is the pointer type.
@Taylor Holliday
thanks @bjorn3 ! What about the stack_store
instruction? Looks like I want to call StackStore
but not sure how (https://docs.rs/cranelift-codegen/latest/cranelift_codegen/ir/trait.InstBuilder.html#method.StackStore)
You might want to use InstBuilder::stack_store
instead, which is the more friendly version of that API
There you don't need to specify a pointer type. And can just provide v1
(from the example above) as a value and ss0
as the stack slot
excellent, thanks @Afonso Bordado !
The CamelCase methods on InstBuilder
should only be used in very specific cases. Rather than building an instruction with a certain opcode, like the snake_case methods do, the CamelCase methods build instructions with specific instruction formats. A single instruction format may be used by multiple opcodes. For example the Binary
instruction format is used by iadd
, isub
, umul
, smul
, udiv
, sdiv
, and so on. Instruction formats are an artifact of the way we internally encode instructions. You shouldn't have to worry about them unless you are writing an optimization pass.
cool... thanks for the info! I'm not sure how I missed the stack_store
function when searching around
I'm also wondering how to represent a struct. In LLVM I could build up a struct type, but I guess cranelift IR doesn't have structs?
@Taylor Holliday we had a recent discussion on this here, but briefly, that's correct: we don't support structs, only primitive types and pointers, but with those you can build structs at a higher abstraction level (i.e. your compiler) and lower to CLIF
cool... thanks @Chris Fallin ! I'm a graphics person attempting a compiler hah
you wouldn't be the first person taking that path! :-) Best of luck and feel free to ask more questions here
A quick search suggests that you are the developer of Audulus. Do you want to use Cranelift as jit compiler for the audio graph?
@bjorn3 yep! It's gonna be tricky though since Apple forbids JIT on iOS. I was thinking of using a wasm interpreter. I'm also working on a little language for writing DSP code inside the nodes in Audulus: https://github.com/audulus/lyte
Yeah, apple forbidding jitting is a pain.
By the way I noticed a typo in the readme: langauge -> language
ah thanks!
Is that language meant to be directly used by end users or will audulus have a fixed set of nodes written in that language that are then composed together using a gui?
directly by end users
currently there is a Lua DSP node in the app
which works reasonably well with LuaJIT
I just figured a statically typed language would be better, plus its an excuse for me to make one
In that case you can't precompile all nodes at build time for the ios version. Precompiling nodes + interpreting the composition of nodes would likely be faster than interpreting the individual instructions of a node. If there are nodes written in that language maybe you could still precompile just those fixed nodes and interpret the rest.
there's also an expression node (simple C math expressions) that would benefit from JITting
so actually what I currently do, which is a little nuts, is I generate C for various subgraphs in the library of modules that come with the app. when the app detects such a configuration by checksumming generated C, it will use an AOT compiled function
that was the best I could do to work around the JIT restriction so far
anyway the app is also on macOS so I can use JITting there
and I want to bring back my Linux and Windows versions at some point, but I digress :)
I know WebkitView doesn't allow jitting, but does SafariView allow jitting? If so I guess you could from a technical perspective try to compile the graph to webassembly and inject it into the SafariView. But the docs say that it isn't allowed to be hidden, so it wouldn't pass the app store review.
oh that's interesting... where's that in the docs?
If you are asking about it not being allowed by the app store guidelines: https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller
In accordance with App Store Review Guidelines, this view controller must be used to visibly present information to users; the controller may not be hidden or obscured by other views or layers.
For jit support in safariview I found this, but I'm not sure if that is the default or requires a special entitlement only given to browsers: https://infrequently.org/2021/08/webkit-ios-deep-dive/
Safari on iOS had JIT for many years, whereas competing browsers were prevented from approaching similar levels of performance. More recently, WebView browsers on iOS have been able to take advantage of WebKit's JIT-ing JavaScript engine, but are prevented from bringing their own.
And it seems that jit compilation is possible for sideloaded apps: https://9to5mac.com/2020/11/06/ios-14-2-brings-jit-compilation-support-which-enables-emulation-apps-at-full-performance/
I don't think any of this is realistically usable for a regular app store app though unfortunately.
yeah not much one can do about it really
thanks for the links!
the Infrequently Noted article seems like an excellent treatment of the topic
can I generate code to get the address of a cranelift_frontend::Variable
? Trying to do lvalues
hehe GPT thought I could pass in a Variable
instead of a StackSlot
: self.builder.ins().stack_addr(I32, variable, 0)
A Variable
doesn't exist in memory; you'll want to create a stackslot if you need a thing that has an address (and then generate loads and stores to it as appropriate)
(also just fwiw, I'm pretty annoyed with GPT/LLMs' tendency to hallucinate incorrect things, so I'm really not wild about helping to fix its incorrect output; as a policy I'd prefer to offer help to human intelligences only!)
ah ok... think I figured it out. Not sure I'm doing it the right way (I'm not using stack slots at all). Instead I created a separate function for codegen on lvalues which returns a Variable. That may not be doable for more complex lvalue expressions like my_struct.my_array[3]
or something. I assume at some point I'll need to generate an address from a stack slot
I've been mainly working from the cranelift jit demo (https://github.com/bytecodealliance/cranelift-jit-demo), but the toy language only has ints, so assignment is very simple
now I'm wondering where to look for an example of exposing a rust function to be called from the generated code
hmm this panics:
fn setup_library(&mut self) {
let mut sig = self.module.make_signature();
sig.params.push(AbiParam::new(types::I32));
let func_id = self.module
.declare_function("lyte_assert", Linkage::Import, &sig)
.unwrap();
self.module.define_function(
func_id,
&mut self.ctx,
).unwrap(); // <--- this panics with InvalidImportDefinition("lyte_assert")
}
anything obvious there? I can do a more complete repro if necessary
@bjorn3 would have a more definitive answer (I don't know cranelift-module
well) but I think one is not supposed to define a function that is also an import
ah ok
If you're only trying to JIT, then an easy way is to make the rust function extern "C"
, and then grab its function pointer as an i64
. Then you can just load it using iconst()
and use that as a value to call_indirect
. Just have to make sure you use the system default calling convention too, or else they'll mismatch.
dang that's pretty easy :)
that's also going to be approximately the most efficient option, because a call from JIT code to some other arbitrary code in the process will have to use a full 64-bit address anyway (not within the 2GB RIP-relative range of an ordinary call instruction); so it's not bad at all!
You can also call .symbol("lyte_assert", the_lyte_assert_function as usize as *const u8)
on the JITBuilder
before you create the JITModule
using it.
.symbol()
takes precedence over dlsym()
for symbol lookup.
I did something dumb, but should cranelift panic like this?
function u0:0() system_v {
block0:
v0 = iconst.i32 1
v1 = load.i32 v0 ; v0 = 1
v2 = iconst.i32 0
return
}
thread 'compiler::tests::basic2' panicked at 'internal error: entered unreachable code: no rule matched for term to_amode at src/isa/x64/inst.isle line 1076; should it be partial?', /Users/holliday/lyte/target/debug/build/cranelift-codegen-db8482b6e5c711c6/out/isle_x64.rs:2297:5
If you run the verifier does it throw an error?
Backends work under the assumption that the verifier passed.
In your case the issue is that you are using a 32bit address on a 64bit arch. If the verifier doesn't catch this even when passing the TargetIsa to verify_function, this is an issue in the verifier.
I was running verify_function
The second argument to verify_function can be flags or it can be a TargetIsa. You are passing flags, but the pointer type check only works when you pass it the TargetIsa for which you are compiling.
Last updated: Dec 23 2024 at 13:07 UTC