Skip to content

Commit

Permalink
Introduce code relocation to avoid the need for far jumps. (#3371)
Browse files Browse the repository at this point in the history
The existing solution for far jumps which can't fit in an immediate is
to convert them to dynamic jumps, storing the destination address in the
data section.

This change helps avoid far jumps altogether by moving large blocks to
beyond the limit to keep immediate mode control flow.
  • Loading branch information
otrho authored Nov 29, 2022
1 parent 8861022 commit e5dc94e
Show file tree
Hide file tree
Showing 11 changed files with 328 additions and 71 deletions.
277 changes: 245 additions & 32 deletions sway-core/src/asm_generation/allocated_abstract_instruction_set.rs

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions sway-core/src/asm_generation/programs/allocated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ impl AllocatedProgram {
.collect(),
};

let (realized_ops, mut label_offsets) =
abstract_ops.realize_labels(&mut self.data_section)?;
let (realized_ops, mut label_offsets) = abstract_ops
.relocate_control_flow(&self.data_section)
.realize_labels(&mut self.data_section)?;
let ops = InstructionSet {
ops: realized_ops.pad_to_even(),
};
Expand All @@ -23,7 +24,10 @@ impl AllocatedProgram {
.entries
.into_iter()
.map(|(selector, label, name)| {
let offset = label_offsets.remove(&label).expect("no offset for entry");
let offset = label_offsets
.remove(&label)
.expect("no offset for entry")
.offs;
(selector, offset, name)
})
.collect();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[[package]]
name = 'far_jumps'
name = 'many_blobs'
source = 'member'
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "many_blobs"
implicit-std = false

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
script;

// Having > 256K NOPs from the BLOBs will force some relocation to keep control flow under the 1MB
// boundary. But only one of these need to be moved to get it back under, and it should be the
// largest one.

fn main() -> u64 {
asm() {
blob i50000;
}
if t() {
if f() {
asm() {
blob i51000;
}
111
} else {
// This one.
asm() {
blob i60000;
}
222
}
} else {
if f() {
asm() {
blob i52000;
}
333
} else {
asm() {
blob i53000;
}
444
}
}
}

fn f() -> bool {
asm() {
zero: bool
}
}

fn t() -> bool {
asm() {
one: bool
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
category = "run"
expected_result = { action = "return", value = 222 }
validate_abi = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[package]]
name = 'single_blob'
source = 'member'
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "far_jumps"
name = "single_blob"
implicit-std = false
45 changes: 11 additions & 34 deletions test/src/ir_generation/tests/far_jumps.sw
Original file line number Diff line number Diff line change
Expand Up @@ -47,40 +47,17 @@ fn b() -> bool {

// regex: REG=\$r\d+

// We want to see the blob and then no JNZI or JNEI.
// check: blob
// not: jnzi
// not: jnei

// The maximum offset available with 18 bits is 262144. So JNZI can't jump to an address larger
// than this. Below those larger addresses are stored in the data section, and they're matched with
// i2622xx (specifically i2622$()), giving a bit of leeway in case slight changes to code gen move
// things around slightly.

// Calling t() - save $reta from the data section, but we're still able to call directly with JI.
// check: lw $$$$reta data_0
// check: ji i2622$()
// This test is solved via code relocation. So a() and the blob should be moved to beyond the
// control flow and keep the rest of b() in the sub 1MB space.

// Some local control flow using addresses from the data section.
// check: lw $$$$tmp data_1
// nextln: jne $REG $$zero $$$$tmp
// Some conditional flow.
// check: jnzi

// Calling t() again.
// check: lw $$$$reta data_2
// check: ji i2622$()
// The guts of a() with the blob.
// check: blob i262200
// check: movi $(ret_reg=$REG) i1
// check: move $$$$retv $ret_reg

// check: lw $$$$tmp data_3
// nextln: jne $REG $$zero $$$$tmp

// check: lw $$$$reta data_4
// check: ji i2622$()

// check: lw $$$$reta data_5
// check: ji i2622$()

// check: data_0 .word 2622$()
// check: data_1 .word 2622$()
// check: data_2 .word 2622$()
// check: data_3 .word 2622$()
// check: data_4 .word 2622$()
// check: data_5 .word 2622$()
// Now we don't want to see any conditional control flow.
// not: jnzi
// not: jnei

0 comments on commit e5dc94e

Please sign in to comment.