Skip to content
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

ASM blocks handling: inlining, DCE, ASM returns without return register #6404

Merged
merged 2 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions docs/book/src/advanced/assembly.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Inline Assembly in Sway

While many users will never have to touch assembly language while writing sway code, it is a powerful tool that enables many advanced use-cases (e.g., optimizations, building libraries, etc).
While many users will never have to touch assembly language while writing Sway code, it is a powerful tool that enables many advanced use-cases (e.g., optimizations, building libraries, etc).

## ASM Block

Expand All @@ -11,7 +11,7 @@ asm() {...}
```

Declaring an `asm` block is similar to declaring a function.
We can specify register names to operate on as arguments, we can perform operations within the block, and we can return a value.
We can specify register names to operate on as arguments, we can perform assembly instructions within the block, and we can return a value by specifying a return register.
Here's an example showing what this might look like:

```sway
Expand All @@ -23,7 +23,12 @@ pub fn add_1(num: u32) -> u32 {
}
```

An `asm` block can only return a single register. If you really need to return more than one value, you can modify a tuple. Here's an example showing how you can implement this `(u64, u64)`:
The return register is specified at the end of the `asm` block, after all the assembly instructions. It consists of the register name and an optional return type. In the above example, the return register name is `r2` and the return type is `u32`.
If the return type is omitted, it is `u64` by default.

The return register itself is optional. If it is not specified, similar to functions, the returned value from the `asm` block will be [unit](../basics/built_in_types.md#unit-type), `()`.

An `asm` block can only return a single register. If you really need to return more than one value, you can modify a tuple. Here's an example showing how you can implement this for `(u64, u64)`:

```sway
{{#include ../../../../examples/asm_return_tuple_pointer/src/main.sw}}
Expand All @@ -37,7 +42,7 @@ Note that in the above example:
- we declared a second register `r2` (you may choose any register names you want).
- we use the `add` opcode to add `one` to the value of `r1` and store it in `r2`.
- `one` is an example of a "reserved register", of which there are 16 in total. Further reading on this is linked below under "Semantics".
- we return `r2` & specify the return type as being u32 (the return type is u64 by default).
- we return `r2` and specify the return type as being `u32`.

An important note is that the `ji` and `jnei` opcodes are not available within an `asm` block. For those looking to introduce control flow to `asm` blocks, it is recommended to surround smaller chunks of `asm` with control flow (`if`, `else`, and `while`).

Expand Down
20 changes: 20 additions & 0 deletions docs/book/src/basics/built_in_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Sway is a statically typed language. At compile time, the types of every value m
<!-- prim_types:example:start -->
Sway has the following primitive types:

1. `()` (unit type)
1. `u8` (8-bit unsigned integer)
1. `u16` (16-bit unsigned integer)
1. `u32` (32-bit unsigned integer)
Expand All @@ -26,6 +27,25 @@ Sway has the following primitive types:
All other types in Sway are built up of these primitive types, or references to these primitive types. You may notice that there are no signed integers&mdash;this is by design. In the blockchain domain that Sway occupies, floating-point values and negative numbers have smaller utility, so their implementation has been left up to libraries for specific use cases.
<!-- prim_types:example:end -->

## Unit Type

The unit type, `()`, is a type that allows only one value, and thus, represents a value with no information. It is used to indicate the absence of a meaningful value, or the result of a function that performs an action, but does not return any data. The value of the unit type, called simply unit, has the same symbol as the unit type, `()`. Unit type in Sway serves a similar purpose as `void` in imperative languages like C or Java.

For example:

```Sway
fn returns_unit() -> () { // Here, `()` represent the unit type.
() // Here, `()` represents the single unit value of the unit type.
}
```

In Sway, if the function return type is not specified, it is `()` by default. Thus, the above example is semantically same as the following:

```Sway
fn returns_unit() {
}
```

## Numeric Types

All of the unsigned integer types are numeric types.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl AbstractInstructionSet {
for idx in dead_jumps {
self.ops[idx] = Op {
opcode: Either::Left(VirtualOp::NOOP),
comment: "removed redundant JUMP".into(),
comment: "remove redundant jump operation".into(),
owning_span: None,
};
}
Expand Down Expand Up @@ -105,7 +105,7 @@ impl AbstractInstructionSet {
for idx in dead_moves {
self.ops[idx] = Op {
opcode: Either::Left(VirtualOp::NOOP),
comment: "removed redundant MOVE".into(),
comment: "remove redundant move operation".into(),
owning_span: None,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,14 @@ impl AllocatedAbstractInstructionSet {
if mask_l.value != 0 {
new_ops.push(AllocatedAbstractOp {
opcode: Either::Left(AllocatedOpcode::PSHL(mask_l)),
comment: "Save registers 16..40".into(),
comment: "save registers 16..40".into(),
owning_span: op.owning_span.clone(),
});
}
if mask_h.value != 0 {
new_ops.push(AllocatedAbstractOp {
opcode: Either::Left(AllocatedOpcode::PSHH(mask_h)),
comment: "Save registers 40..64".into(),
comment: "save registers 40..64".into(),
owning_span: op.owning_span.clone(),
});
}
Expand All @@ -146,14 +146,14 @@ impl AllocatedAbstractInstructionSet {
if mask_h.value != 0 {
new_ops.push(AllocatedAbstractOp {
opcode: Either::Left(AllocatedOpcode::POPH(mask_h)),
comment: "Restore registers 40..64".into(),
comment: "restore registers 40..64".into(),
owning_span: op.owning_span.clone(),
});
}
if mask_l.value != 0 {
new_ops.push(AllocatedAbstractOp {
opcode: Either::Left(AllocatedOpcode::POPL(mask_l)),
comment: "Restore registers 16..40".into(),
comment: "restore registers 16..40".into(),
owning_span: op.owning_span.clone(),
});
}
Expand Down Expand Up @@ -299,7 +299,8 @@ impl AllocatedAbstractInstructionSet {
AllocatedRegister::Constant(ConstantRegister::InstructionStart),
),
owning_span: owning_span.clone(),
comment: "Get current instruction offset from Instruction start".into(),
comment: "get current instruction offset from instructions start ($is)"
.into(),
});
realized_ops.push(RealizedOp {
opcode: AllocatedOpcode::SRLI(
Expand All @@ -308,7 +309,7 @@ impl AllocatedAbstractInstructionSet {
VirtualImmediate12 { value: 2 },
),
owning_span: owning_span.clone(),
comment: "Current instruction offset in 32b words".into(),
comment: "get current instruction offset in 32-bit words".into(),
});
realized_ops.push(RealizedOp {
opcode: AllocatedOpcode::ADDI(r1.clone(), r1, imm),
Expand Down Expand Up @@ -540,7 +541,7 @@ impl AllocatedAbstractInstructionSet {
if rel_offset(lab) == 0 {
new_ops.push(AllocatedAbstractOp {
opcode: Either::Left(AllocatedOpcode::NOOP),
comment: "NOP for self loop".into(),
comment: "emit noop for self loop".into(),
owning_span: None,
});
new_ops.push(op);
Expand Down Expand Up @@ -583,7 +584,7 @@ impl AllocatedAbstractInstructionSet {
if rel_offset(lab) == 0 {
new_ops.push(AllocatedAbstractOp {
opcode: Either::Left(AllocatedOpcode::NOOP),
comment: "NOP for self loop".into(),
comment: "emit noop for self loop".into(),
owning_span: None,
});
new_ops.push(op);
Expand Down Expand Up @@ -628,7 +629,7 @@ impl AllocatedAbstractInstructionSet {
if rel_offset(lab) <= consts::TWELVE_BITS {
new_ops.push(op)
} else {
panic!("Return to address must be right after the call for which we saved this addr.");
panic!("Return to address must be right after the call for which we saved this address.");
}
}

Expand Down
Loading
Loading