-
Notifications
You must be signed in to change notification settings - Fork 59
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
enable creating invalid programs #147
base: master
Are you sure you want to change the base?
Changes from 2 commits
9843573
3292618
5ab1abf
3ff5cb0
12f2571
415e6f7
a2db545
638f0d0
a092319
1d4feb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ use alloc::vec::Vec; | |
use core::ops::Range; | ||
|
||
#[derive(Copy, Clone)] | ||
struct InstructionBuffer { | ||
pub struct InstructionBuffer { | ||
bytes: [u8; program::MAX_INSTRUCTION_LENGTH], | ||
length: u8, | ||
} | ||
|
@@ -60,9 +60,27 @@ impl Instruction { | |
} | ||
} | ||
|
||
#[derive(Copy, Clone)] | ||
pub enum InstructionOrBytes { | ||
Instruction(Instruction), | ||
Raw(InstructionBuffer), | ||
} | ||
|
||
impl From<Instruction> for InstructionOrBytes { | ||
fn from(value: Instruction) -> Self { | ||
Self::Instruction(value) | ||
} | ||
} | ||
|
||
impl From<InstructionBuffer> for InstructionOrBytes { | ||
fn from(value: InstructionBuffer) -> Self { | ||
Self::Raw(value) | ||
} | ||
} | ||
|
||
#[derive(Copy, Clone)] | ||
struct SerializedInstruction { | ||
instruction: Instruction, | ||
instruction: Option<Instruction>, | ||
bytes: InstructionBuffer, | ||
target_nth_instruction: Option<usize>, | ||
position: u32, | ||
|
@@ -120,7 +138,8 @@ impl ProgramBlobBuilder { | |
self.exports.push((target_basic_block, ProgramSymbol::new(symbol.into()))); | ||
} | ||
|
||
pub fn set_code(&mut self, code: &[Instruction], jump_table: &[u32]) { | ||
subotic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub fn set_code(&mut self, code: &[impl Into<InstructionOrBytes> + Copy], jump_table: &[u32]) { | ||
let code: Vec<InstructionOrBytes> = code.iter().map(|inst| (*inst).into()).collect(); | ||
fn mutate<T>(slot: &mut T, value: T) -> bool | ||
where | ||
T: PartialEq, | ||
|
@@ -137,8 +156,10 @@ impl ProgramBlobBuilder { | |
basic_block_to_instruction_index.push(0); | ||
|
||
for (nth_instruction, instruction) in code.iter().enumerate() { | ||
if instruction.opcode().starts_new_basic_block() { | ||
basic_block_to_instruction_index.push(nth_instruction + 1); | ||
if let InstructionOrBytes::Instruction(inst) = instruction { | ||
if inst.opcode().starts_new_basic_block() { | ||
basic_block_to_instruction_index.push(nth_instruction + 1); | ||
} | ||
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. @koute This will pose a problem, if we ever would like to add a label to the instruction when writing a program in assembler, as 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. Hm, okay, good point. In that case maybe we could add a flag to the raw bytes to specify whether it's supposed to start a new basic block? 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. Hm, yes. I will try it out and report back. |
||
} | ||
} | ||
|
||
|
@@ -148,21 +169,32 @@ impl ProgramBlobBuilder { | |
let mut instructions = Vec::new(); | ||
let mut position: u32 = 0; | ||
for (nth_instruction, instruction) in code.iter().enumerate() { | ||
let mut instruction = *instruction; | ||
let target = instruction.target_mut(); | ||
let target_nth_instruction = target.map(|target| { | ||
let target_nth_instruction = basic_block_to_instruction_index[*target as usize]; | ||
|
||
// This is completely inaccurate, but that's fine. | ||
*target = position.wrapping_add((target_nth_instruction as i32 - nth_instruction as i32) as u32); | ||
target_nth_instruction | ||
}); | ||
|
||
let entry = SerializedInstruction { | ||
instruction, | ||
bytes: InstructionBuffer::from((position, instruction)), | ||
target_nth_instruction, | ||
position, | ||
let entry = match instruction { | ||
InstructionOrBytes::Instruction(mut instruction) => { | ||
let target = instruction.target_mut(); | ||
let target_nth_instruction = target.map(|target| { | ||
let target_nth_instruction = basic_block_to_instruction_index[*target as usize]; | ||
|
||
// This is completely inaccurate, but that's fine. | ||
*target = position.wrapping_add((target_nth_instruction as i32 - nth_instruction as i32) as u32); | ||
target_nth_instruction | ||
}); | ||
|
||
SerializedInstruction { | ||
instruction: Some(instruction), | ||
bytes: InstructionBuffer::from((position, instruction)), | ||
target_nth_instruction, | ||
position, | ||
} | ||
} | ||
// The instruction in the form of raw bytes, that should only be appended, as we want to | ||
// be able to slip in invalid instructions, e.g., jump instruction with an invalid offset | ||
InstructionOrBytes::Raw(bytes) => SerializedInstruction { | ||
instruction: None, | ||
bytes: *bytes, | ||
target_nth_instruction: None, | ||
position, | ||
}, | ||
}; | ||
|
||
position = position.checked_add(entry.bytes.len() as u32).expect("too many instructions"); | ||
|
@@ -178,9 +210,11 @@ impl ProgramBlobBuilder { | |
|
||
if let Some(target_nth_instruction) = instructions[nth_instruction].target_nth_instruction { | ||
let new_target = instructions[target_nth_instruction].position; | ||
if mutate(instructions[nth_instruction].instruction.target_mut().unwrap(), new_target) || modified { | ||
instructions[nth_instruction].bytes = | ||
InstructionBuffer::from((position, instructions[nth_instruction].instruction)); | ||
|
||
if let Some(mut instruction) = instructions[nth_instruction].instruction { | ||
if mutate(instruction.target_mut().unwrap(), new_target) || modified { | ||
instructions[nth_instruction].bytes = InstructionBuffer::from((position, instruction)); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -254,6 +288,9 @@ impl ProgramBlobBuilder { | |
|
||
self.bitmask = bitmask.finish(); | ||
|
||
log::debug!("code: {:?}", self.code); | ||
log::debug!("bitmask: {:?}", self.bitmask); | ||
|
||
self.basic_block_to_instruction_index = basic_block_to_instruction_index; | ||
self.instruction_index_to_code_offset = instructions.iter().map(|entry| entry.position).collect(); | ||
|
||
|
@@ -265,13 +302,16 @@ impl ProgramBlobBuilder { | |
parsed.push((instruction.offset, instruction.kind)); | ||
offsets.insert(instruction.offset); | ||
} | ||
|
||
assert_eq!(parsed.len(), instructions.len()); | ||
|
||
for ((offset, mut instruction), entry) in parsed.into_iter().zip(instructions.into_iter()) { | ||
assert_eq!(instruction, entry.instruction, "broken serialization: {:?}", entry.bytes.bytes); | ||
assert_eq!(entry.position, offset); | ||
if let Some(target) = instruction.target_mut() { | ||
assert!(offsets.contains(target)); | ||
if let Some(entry_instruction) = entry.instruction { | ||
// @Jan: Don't know why this is allways failing | ||
// assert_eq!(instruction, entry_instruction, "broken serialization: {:?}", entry.bytes.bytes); | ||
assert_eq!(entry.position, offset); | ||
if let Some(target) = instruction.target_mut() { | ||
assert!(offsets.contains(target)); | ||
} | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
pub @main: | ||
pub @expected_exit: | ||
a0 = 1234, jump -25 | ||
trap | ||
a1 = 0xdeadbeef |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
pub @main: | ||
a0 = 1234, jump +6 | ||
trap | ||
a1 = 0xdeadbeef |
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 should enforce that there's either a
+
or a-
there at the start. (To make it explicit that this is supposed to be a relative offset, and not an absolute one like if a label is used.)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.
Ah, got it now. Will make the parsing more strict.