├── .github └── workflows │ ├── markdown.yml │ ├── rust.yml │ └── yaml.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── examples ├── cross_func_boundries.rs ├── fizzbuzz.rs └── simple.rs └── src └── lib.rs /.github/workflows/markdown.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Markdown CI 3 | 4 | # yamllint disable-line rule:truthy 5 | on: [push, pull_request] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Install MDL 15 | run: sudo gem install mdl 16 | - name: Lint README 17 | run: mdl -- $(find . -name '*.md') 18 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Rust 3 | 4 | # yamllint disable-line rule:truthy 5 | on: [push, pull_request] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ${{ matrix.os }} 14 | 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest] 18 | rust: [stable, beta, "1.59.0"] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - uses: actions-rs/toolchain@v1 24 | with: 25 | toolchain: ${{ matrix.rust }} 26 | override: true 27 | components: rustfmt, clippy 28 | 29 | - name: Lint 30 | run: cargo fmt -- --check 31 | 32 | - name: Run Clippy 33 | run: cargo clippy --all-features -- -D warnings 34 | 35 | - name: Run tests 36 | run: cargo test --verbose 37 | 38 | - name: Build 39 | run: cargo build --verbose 40 | 41 | - name: Build documentation 42 | run: cargo doc --verbose 43 | -------------------------------------------------------------------------------- /.github/workflows/yaml.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Yaml CI 3 | 4 | # yamllint disable-line rule:truthy 5 | on: [push, pull_request] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Install yamllint 15 | run: sudo apt install yamllint 16 | - name: Lint yaml files 17 | run: yamllint -- $(find . -name '*.yml' -or -name '*.yaml') 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.swp 3 | *.s 4 | *.o 5 | *.exe 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "goto-label" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "goto-label" 3 | authors = ["Property404 "] 4 | description = "`goto` implementation for Rust" 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | repository = "https://github.com/Property404/goto-label-rs" 8 | rust-version = "1.59.0" 9 | version = "0.1.0" 10 | 11 | [dependencies] 12 | 13 | [profile.release] 14 | # If you use this crate as a dependency, you should add this line in your Cargo.toml 15 | # otherwise your code WILL segfault 16 | opt-level = 1 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Goto/Label for Rust 2 | 3 | Tired of using newfangled control flow mechnisms like "loop," "while," 4 | and "for"? 5 | 6 | Well worry no more! Finally, "goto" and "label" macros have arrived for Rust! 7 | And they're `#![no_std]`! 8 | 9 | ```rust 10 | use goto_label::{goto, label}; 11 | 12 | #[no_mangle] // Needed to prevent foo() from being optimized away 13 | unsafe fn foo() { 14 | println!("This text will never be printed!"); 15 | 16 | label!("label1"); 17 | print!("Hello"); 18 | goto!("label2"); 19 | 20 | println!("Neither will this be printed!"); 21 | } 22 | 23 | unsafe fn hello_world() { 24 | goto!("label1"); 25 | println!("This won't be printed either!"); 26 | 27 | label!("label2"); 28 | println!(" World!"); 29 | } 30 | 31 | unsafe { 32 | hello_world(); 33 | } 34 | ``` 35 | 36 | ## Currently supported Architectures 37 | 38 | * x86 39 | * x86\_64 40 | * aarch64 41 | 42 | ## Warning 43 | 44 | Do not actually use this crate. It will definitely cause 45 | undefined behavior, most likely manifesting as segfaults. 46 | 47 | ## See also 48 | 49 | [Another goto implementation](https://github.com/clucompany/Goto) 50 | -------------------------------------------------------------------------------- /examples/cross_func_boundries.rs: -------------------------------------------------------------------------------- 1 | use goto_label::{goto, label}; 2 | 3 | #[no_mangle] // Needed to prevent foo() from being optimized away 4 | unsafe fn foo() { 5 | println!("This text will never be printed!"); 6 | 7 | label!("label1"); 8 | print!("Hello"); 9 | goto!("label2"); 10 | 11 | println!("Neither will this be printed!"); 12 | } 13 | 14 | unsafe fn hello_world() { 15 | goto!("label1"); 16 | println!("This won't be printed either!"); 17 | 18 | label!("label2"); 19 | println!(" World!") 20 | } 21 | 22 | fn main() { 23 | unsafe { 24 | hello_world(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/fizzbuzz.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_assignments)] 2 | use goto_label::{goto, label}; 3 | 4 | fn fizzbuzz() { 5 | let mut n = 1; 6 | 7 | unsafe { 8 | goto!("fizzbuzz_start"); 9 | 10 | label!("print_fizzbuzz"); 11 | println!("FizzBuzz"); 12 | goto!("fizzbuzz_return"); 13 | 14 | label!("print_fizz"); 15 | println!("Fizz"); 16 | goto!("fizzbuzz_return"); 17 | 18 | label!("print_buzz"); 19 | println!("Buzz"); 20 | goto!("fizzbuzz_return"); 21 | 22 | label!("fizzbuzz_start"); 23 | 24 | if (n % 3 == 0) && (n % 5 == 0) { 25 | goto!("print_fizzbuzz"); 26 | } else if n % 3 == 0 { 27 | goto!("print_fizz"); 28 | } else if n % 5 == 0 { 29 | goto!("print_buzz"); 30 | } else { 31 | println!("{n}"); 32 | } 33 | 34 | label!("fizzbuzz_return"); 35 | if n < 15 { 36 | n += 1; 37 | goto!("fizzbuzz_start"); 38 | } 39 | } 40 | } 41 | 42 | fn main() { 43 | fizzbuzz(); 44 | } 45 | -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | use goto_label::{goto, label}; 2 | 3 | fn main() { 4 | unsafe { 5 | goto!("label0"); 6 | } 7 | 8 | println!("This will be skipped"); 9 | 10 | unsafe { 11 | label!("label0"); 12 | } 13 | 14 | println!("Hello world!"); 15 | } 16 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![warn(missing_docs)] 3 | //! `goto` and `label` for Rust! 4 | //! Now you never have to use `while`, `for`, or `loop` again! 5 | //! 6 | //! # Example 7 | //! ``` 8 | //! use goto_label::{goto, label}; 9 | //! 10 | //! #[no_mangle] // Needed to prevent foo() from being optimized away 11 | //! unsafe fn foo() { 12 | //! println!("This text will never be printed!"); 13 | //! 14 | //! label!("label1"); 15 | //! print!("Hello"); 16 | //! goto!("label2"); 17 | //! 18 | //! println!("Neither will this be printed!"); 19 | //! } 20 | //! 21 | //! unsafe fn hello_world() { 22 | //! goto!("label1"); 23 | //! println!("This won't be printed either!"); 24 | //! 25 | //! label!("label2"); 26 | //! println!(" World!"); 27 | //! } 28 | //! 29 | //! unsafe { 30 | //! hello_world(); 31 | //! } 32 | //! ``` 33 | 34 | /// Create a label 35 | /// 36 | /// This will create a linker symbol. Be careful that the label you use does not clash with other 37 | /// symbols. 38 | /// 39 | /// # Example 40 | /// ``` 41 | /// use goto_label::label; 42 | /// 43 | /// // Create a label named "foo" 44 | /// unsafe { 45 | /// label!("foo"); 46 | /// } 47 | /// ``` 48 | #[macro_export] 49 | macro_rules! label { 50 | ($label:literal) => { 51 | $crate::might_skip! {{ 52 | #[allow(named_asm_labels)] 53 | #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] 54 | { 55 | use core::arch::asm; 56 | asm!(concat!($label, ":")); 57 | } 58 | #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))] 59 | compile_error!("`label!` not implemented for this architecture!"); 60 | }} 61 | }; 62 | } 63 | 64 | /// Jump to label 65 | /// 66 | /// # Example 67 | /// ``` 68 | /// use goto_label::{goto, label}; 69 | /// 70 | /// unsafe { 71 | /// // Jump to label named "foo" 72 | /// goto!("foo"); 73 | /// println!("This line will never be printed!"); 74 | /// 75 | /// // Label is defined here 76 | /// label!("foo"); 77 | /// } 78 | /// ``` 79 | #[macro_export] 80 | macro_rules! goto { 81 | ($label:literal) => { 82 | $crate::might_skip! {{ 83 | use core::arch::asm; 84 | #[allow(named_asm_labels)] 85 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 86 | { 87 | asm!(concat!("jmp ", $label)); 88 | } 89 | #[cfg(any(target_arch = "aarch64"))] 90 | { 91 | asm!(concat!("b ", $label)); 92 | } 93 | #[cfg(not(any(target_arch = "x86", target_arch = "aarch64", target_arch = "x86_64")))] 94 | compile_error!("`goto!` not implemented for this architecture!"); 95 | }} 96 | }; 97 | } 98 | 99 | // Inform the compiler that this expression might be skipped by `goto!` 100 | // 101 | // This attempts to prevent segfaults in optimized builds by preventing optimization with 102 | // surrounding code. It doesn't work well enough to keep as a documented public macro. 103 | #[macro_export] 104 | #[doc(hidden)] 105 | macro_rules! might_skip { 106 | ($expression:expr) => {{ 107 | #[allow(unused_unsafe)] 108 | if unsafe { 109 | let x = 42; 110 | let y = &x as *const i32; 111 | // This will always be true but the compiler doesn't know that! 112 | core::ptr::read_volatile(y) == core::ptr::read_volatile(y) 113 | } { 114 | $expression 115 | } else { 116 | Default::default() 117 | } 118 | }}; 119 | } 120 | 121 | #[cfg(test)] 122 | mod tests { 123 | #[test] 124 | fn basic() { 125 | unsafe { 126 | let mut x = 0; 127 | assert_eq!(x, 0); 128 | 129 | goto!("end0"); 130 | 131 | x = 42; 132 | 133 | label!("end0"); 134 | assert_eq!(x, 0); 135 | } 136 | } 137 | } 138 | --------------------------------------------------------------------------------