-
Notifications
You must be signed in to change notification settings - Fork 54
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
Dynamic Memory Operands #88
Comments
Hi! I'm thinking about if this is reasonably implementable in dynasm-rs. Dynasm-rs does most of its work at compile time. Basically anything but non-constant immediates, dynamic register support and relocations are resolved at compile time already. This includes things like instruction variant selection and memory reference variant selection. I think instruction variant selection generally just depends on the presence of a memory reference, not its contents, so pushing that logic to compile time might be possible. We'd also need syntax to escape this behavior and emit the correct code to handle this. something like: dynasm!(assembler; mov rax, QWORD [dyn foo]) Would be fine. We can't just use That should then be interpreted by dynasm-rs to insert a piece of code that emits the correct byte(s), or in the case of aarch64, or them with the rest of the instruction word. On the API design, the different architectures have very different requirements. x64/x86 are variable width, even in memory references, so we cannot just insert something like Overall, I think that it is a possible feature to implement, but it definitely isn't the easiest. I hope that helps you a bit. |
Thanks for the reply!
Looking at some common instructions, this generally seems to be the case: the opcode seems to depend on operand order (i.e. is the memory operand source or target) and sometimes on operand size (e.g. However, both operand order and operand size should be known at compile time.
Based on that, I've sketched out the following: // Runtime library
mod dynasmrt::x64 {
enum Scale {
One,
Two,
Four,
Eight,
}
enum Displacement {
Byte(i8),
Word(i16),
DWord(i32),
}
#[derive(Clone, Copy)]
enum MemoryReference<Base: Register, Index: Register> {
Displacement(Displacement),
Base(Base),
BaseDisplacement(Base, Displacement),
BaseIndex(Base, Index, Scale),
BaseIndexDisplacement(Base, Index, Scale, Displacement),
}
impl<Base: Register, Index: Register> MemoryReference<Base, Index> {
fn push_rex_prefix(&self, assembler: &mut Assembler) {
// TODO How to determine whether REX prefix is needed? Method on Register?
}
fn push_operands(&self, assembler: &mut Assembler) {
// ...
}
}
trait MemoryReferencable {
fn reference() -> MemoryReference;
}
}
// Syntax
dynasm!(assembler
; mov rax, [dyn foo]
; mov QWORD [dyn bar], rax);
// Generated code for x64
(foo as MemoryReferencable).reference().push_rex_prefix(assembler);
assembler.push_u8(0x8B);
(foo as MemoryReferencable).reference().push_operands(assembler);
(bar as MemoryReferencable).reference().push_rex_prefix(assembler);
assembler.push_u8(0x89);
(bar as MemoryReferencable).reference().push_operands(prefix); However, I think you'd need something to know whether to actually emit a REX prefix. I guess a method a la trait Register {
fn needs_rex(&self) -> bool
} would work? |
Damn, I hadn't even thought about that register bits also might need to end up in the REX prefix. That makes it even messier. So this'd require possibly dynamic rewriting of the REX / VEX / XOP prefixes, possible changes to the modrm byte, and conditional emitting of a SIB byte and a displacement in x64 mode. That's quite a few changes, and that's only for one of the architecture. I'm not sure if I'd recommend putting in all that work to be honest. Right now you can already get quite far using dynamic registers. It's definitely possible, but I'm wondering if at that point you'd be better off just lifting all of the logic to runtime to simplify interactions, as this would add even more special cases to the compile time and run-time interaction. |
Greetings!
I am currently porting a simple JIT, used in a compiler construction class, from C++ to Rust. In C++, I used the asmjit project.
In asmjit, memory operands and registers can be used as values, i.e. I can assign a memory operands to a variable and later use it in an assembler call
If I want to use a value stored in memory at the moment, I have to repeat
I.e. I have to repeat the memory reference each time.
It would be really handy to have a feature similar to the asmjit one in this crate, i.e. being able to do something like
Because then you could encapsulate where certain values are stored (relative to
rbp
, relative torsp
, etc.) in the Trait Implementation.I do not know too much about Rust macros, so I apologize if something like this is not feasible to implement.
On the other hand, I'd be willing to tinker with this myself, I would just need a pointer (or reference) in the right direction.
Best regards!
The text was updated successfully, but these errors were encountered: