---
# System prepended metadata

title: gccrs's inline asm mov issue

---

# gccrs's inline asm mov issue

Hi everyone, this article details the progress on the inline asm project in gccrs as well as asks for help debugging the issue of tree IR in generating assembly.

The write up contains C and rust test file, tree IR generation for both test file, with diff between, along with assembly.

## Project structure
The related branch is `asm_generic_il` PR https://github.com/Rust-GCC/gccrs/pull/3060

Since there might be a lot of force pushes, the related identifying title in the PR is `
Use's array type when constring string tree `

The related files for this issue includes 
- gcc/rust/expand/rust-macrobuiltins-asm.h/cc: Where the parsing of an `asm!()` happens.
- gcc/rust/ast/rust-expr.h: Where the ast of an inline asm is defined.
- gcc/rust/hir/tree/rust-hir-expr.h: Where the HIR (high level ir) of an inline asm is defined.
- gcc/rust/backend/rust-compile-asm.h/cc: where the backend of inline asm starts processing.

The progression of an `asm!` approximately looks like this

```mermaid
flowchart TD
%% Nodes
A("asm!"):::green
B("parsing"):::orange
C("AST"):::blue
D{"HIR"}:::yellow
E("backend tree"):::pink
F("gimplifier"):::purple
G("assembly code")


%% Edges
A --> B -->|Via rust-macrobuiltins.h/cc| C -->|lowering necessary parts| D
D -->|lowering in rust-compile-asm.h/cc| E -->|tree ir| F 
F -->|/not the domain of concern/|G

%% Styling
classDef green fill:#B2DFDB,stroke:#00897B,stroke-width:2px;
classDef orange fill:#FFE0B2,stroke:#FB8C00,stroke-width:2px;
classDef blue fill:#BBDEFB,stroke:#1976D2,stroke-width:2px;
classDef yellow fill:#FFF9C4,stroke:#FBC02D,stroke-width:2px;
classDef pink fill:#F8BBD0,stroke:#C2185B,stroke-width:2px;
classDef purple fill:#E1BEE7,stroke:#8E24AA,stroke-width:2px; 
```

## Assumption and test files

### Assumption
The assumption that gccrs to generate the correct assembly is that for a test file, we look at a `.c` file and a `.rs` file and their tree, gimple and asm output from their respective gcc and gccrs compiler.

- For tree IR, we use `debug` right after we generate a tree asm in gcc and gccrs to inspect them.
- For gimple, we invoke the command line option `-fdump-tree-original` from gcc and gccrs to inspect them.
- For asm output, we look to see if the inline assembly we pass in from the `asm!` invocation is inserted between others assembly.

The end expectation is 
> so i was expecting this test file (rust file) to execute correctly (no error) and that `_x` has the value of 42 in it. This means that in the assembly file, with reg option i'm expecting the %0 in the assembly string to be transformed into a register like x0 or something on ARM

### Test files
The test file from c and rust involves their respective invocation.

For c file `asm_mov.c`:
```c 
#include <stdio.h>

int main() {
  int output1 = 0, output2;

  asm("mov %0, 42\n" // Move constant 42 to output1
      : "=r"(output1));

  return 0;
}
```

For rust file `asm_mov.rs`:
```rust
#![feature(rustc_attrs)]

#[rustc_builtin_macro]
macro_rules! asm {
    () => {};
}
extern "C" {
    fn printf(s: *const i8, ...);
}

fn main() {
    let mut _x: i32 = 0;
    unsafe {
        asm!(
            "mov %0, 42",
            out(reg) _x
        );
    }
}
```
### Test file notes

The assembly string in rust doesn't follow the specs in https://doc.rust-lang.org/reference/inline-assembly.html, refering to \_x as %0 instead of {} 

This is just to guarantee that when the string in `asm!` goes through the tree ir construction and eventually the gimplifier, it is also %0.




## Tree inspection

Files:
- C's output of tree: tree_c.txt
- rust's output of tree: tree_rs.txt
- C's assembly output: asm_mov.s
- rust's assembly output: asm_mov_rs.s
### gcc
We modify the gcc/gccrs codebase so that after each respective compiler produces the tree IR right after they finish building the asm tree expr

For gcc, we modify `c_parser_asm_statement` in `gcc/c/c-parser.cc` to debug the tree, using debug_tree from print-tree

```diff
#include "run-rtl-passes.h"
#include "intl.h"
#include "c-family/name-hint.h"
#include "tree-iterator.h"
+#include "print-tree.h"
#include "tree-pretty-print.h"
#include "memmodel.h"
#include "c-family/known-headers.h"

...
...
...

// In c_parser_asm_statement()
...
  if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
    c_parser_skip_to_end_of_block_or_statement (parser);

  ret = build_asm_stmt (is_volatile,
			build_asm_expr (asm_loc, str, outputs, inputs, clobbers,
					labels, simple, is_inline));
+ debug_tree (ret);
error:
  return ret;

error_close_paren:
  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
  goto error;
}

```

The tree output from the compiler with ` ../gccrs-install/bin/gcc -S asm_mov.c > tree_c.txt 2>&1
`: (we will compare this against gccrs's tree output):

```<
<asm_expr 0xffffb02ad240
    type <void_type 0xffffb02b4f18 void VOID
        align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0xffffb02b4f18
        pointer_to_this <pointer_type 0xffffb02bc000>>
    side-effects
    arg:0 <string_cst 0xffffb0224d20
        type <array_type 0xffffb0222888 type <integer_type 0xffffb02b43f0 char>
            BLK
            size <integer_cst 0xffffb02b7378 constant 96>
            unit-size <integer_cst 0xffffb0200660 constant 12>
            align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0xffffb0222888 domain <integer_type 0xffffb02227e0>>
        readonly constant static "mov %0, 42\012\000">
    arg:1 <tree_list 0xffffb0224d70
        purpose <tree_list 0xffffb0224d48
            value <string_cst 0xffffb0200690 type <array_type 0xffffb02229d8>
                readonly constant static "=r\000">>
        value <var_decl 0xffffb01e32d0 output1 type <integer_type 0xffffb02b45e8 int>
            used read SI asm_mov.c:4:7
            size <integer_cst 0xffffb02b7150 constant 32>
            unit-size <integer_cst 0xffffb02b7168 constant 4>
            align:32 warn_if_not_align:0 context <function_decl 0xffffb0226200 main> initial <integer_cst 0xffffb02b72a0 0>>>
    asm_mov.c:6:3 start: asm_mov.c:6:3 finish: asm_mov.c:6:5>
```

Inspection of the assembly file via `cat asm_mov.s` reveals that the correct strings that is passed into the `__asm__()` call is transformed successfully with the register x0:
```diff
	.arch armv8-a
	.file	"asm_mov.c"
	.text
	.section	.rodata
	.align	3
.LC0:
	.string	"Output 1: %d\n"
	.text
	.align	2
	.global	main
	.type	main, %function
main:
.LFB0:
	.cfi_startproc
	stp	x29, x30, [sp, -32]!
	.cfi_def_cfa_offset 32
	.cfi_offset 29, -32
	.cfi_offset 30, -24
	mov	x29, sp
	str	wzr, [sp, 28]
#APP
// 6 "asm_mov.c" 1
+	mov x0, 42

// 0 "" 2
#NO_APP
	str	w0, [sp, 28]
	ldr	w1, [sp, 28]
	adrp	x0, .LC0
	add	x0, x0, :lo12:.LC0
	bl	printf
	mov	w0, 0
	ldp	x29, x30, [sp], 32
	.cfi_restore 30
	.cfi_restore 29
	.cfi_def_cfa_offset 0
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (GNU) 14.0.1 20240309 (experimental)"
	.section	.note.GNU-stack,"",@progbits
```

### gccrs

For gccrs, all the related functions for generating tree ir is 
in `gcc/rust/backend/rust-compile-asm.cc`. We also use `debug` from the Backend::
```diff
tree
CompileAsm::asm_build_expr (HIR::InlineAsm &expr)
{
  auto asm_expr
    = asm_build_stmt (expr.get_locus (), {asm_construct_string_tree (expr),
					  asm_construct_outputs (expr),
					  asm_construct_inputs (expr),
					  asm_construct_clobber_tree (expr),
					  asm_construct_label_tree (expr)});

  ASM_INPUT_P (asm_expr) = expr.is_simple_asm ();
  ASM_VOLATILE_P (asm_expr) = false;
  ASM_INLINE_P (asm_expr) = expr.is_inline_asm ();
+ Backend::debug (asm_expr);
  return asm_expr;
}
```

gccrs spits out its tree ir:
```<
 <asm_expr 0xffff903c1a00
    type <void_type 0xffff903aef18 VOID
        align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0xffff903aef18
        pointer_to_this <pointer_type 0xffff903b6000>>
    side-effects protected static
    arg:0 <string_cst 0xffff9039b9e0
        type <array_type 0xffff903c3d20 type <integer_type 0xffff903c3c78>
            string-flag BLK
            size <integer_cst 0xffff903b31f8 constant 96>
            unit-size <integer_cst 0xffff903b38b8 constant 12>
            align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0xffff903c3d20 domain <integer_type 0xffff903c3bd0>>
        constant "mov %0, 42\012">
    arg:1 <tree_list 0xffff903bf938
        purpose <tree_list 0xffff903bf910
            value <string_cst 0xffff903b38e8 constant "=r\000">>
        value <var_decl 0xffff903cb090 _x type <integer_type 0xffff903ae5e8 i32>
            used SI asm_mov.rs:12:9
            size <integer_cst 0xffff9039afc0 constant 32>
            unit-size <integer_cst 0xffff9039afd8 constant 4>
            align:32 warn_if_not_align:0 context <function_decl 0xffff903c6100 asm_mov::main> initial <integer_cst 0xffff903b3120 0>>>
    asm_mov.rs:14:9 start: asm_mov.rs:14:9 finish: asm_mov.rs:14:11>
```

With a message of assembler error
```
/tmp/ccv5FWsb.s: Assembler messages:
/tmp/ccv5FWsb.s:15: Error: operand 1 must be an integer register -- `mov %0,42'
```

Inspection shows the string inputted from the `asm!()` macro hasn't been transformed. The %0 hasn't been transformed to x0 in ARM (on my macbook)
```diff 
.arch armv8-a
	.file	"asm_mov.rs"
	.text
	.align	2
	.global	main
	.type	main, %function
main:
.LFB0:
	.cfi_startproc
	sub	sp, sp, #16
	.cfi_def_cfa_offset 16
	str	wzr, [sp, 12]
#APP
// 14 "asm_mov.rs" 1
+	mov %0, 42

// 0 "" 2
#NO_APP
	add	sp, sp, 16
	.cfi_def_cfa_offset 0
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (GNU) 14.0.1 20240309 (experimental)"
	.
```

### diff of trees

The diff of two tree IRs are in the following link
https://www.diffchecker.com/Z10caKQa/

Besides the difference in addresses, for example `0xffffb618d240` versus `0xffffaecd1a00`, there are some notable differences
- side effects: c version doens't have any side effects, it has `protected` and `static`

- rust has `string flag` tag on line 8, before the word BLK?!

- The string in rust version is of type `constant`, where in c, it is `readonly constant static` 
- In arg1, rust's arg's purpose's value is a string cst of constant, where in c, it is `string cst` of `array type` of `readonly constant static` 

- The refered value of `_x` in rust (line 18) is 
### analysis

In gccrs, instead of wrapping an build_stmt around a build_expr, we wrap a build_expr around a build_stmt, just because InlineAsm is an expr. The functionality should stay the same.

*to be continue*


## Revisions

# ASM_INPUT_P to false


Sovled issue, will write credit and name asshole
## Credits

credit: Arthur, Pierre-Emmanuel, Arsen, rsandifo, pinskia (a.k.a @norwoodites)


