bpf: add the addr_space_cast instruction

Message ID 20260929183603.687571-1-vineet.gupta@linux.dev
State New
Headers
Series bpf: add the addr_space_cast instruction |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Vineet Gupta Sept. 29, 2026, 6:36 p.m. UTC
  gas doesn't support bpf arenas as it is missing addr_space_cast BPF
instruction. It is essentially a `mov' with a distinguised offset and
the two address spaces supported (0: generic, 1: arena) ecoded in the
immediate.

To keep implementation simple, spell out the two combinations as literal
template text.  The entries have to precede `mov', whose mask covers only
the opcode byte and would otherwise match first; movs8 and friends are
already written this way.

The instruction is available at every ISA spec (just as clang).

gas/
	* testsuite/gas/bpf/ascast.s: New test.
	* testsuite/gas/bpf/ascast.d: New test.
	* testsuite/gas/bpf/ascast-pseudoc.s: New test.
	* testsuite/gas/bpf/ascast-pseudoc.d: New test.
	* testsuite/gas/bpf/bpf.exp: Run them.

include/
	* opcode/bpf.h (BPF_OFFSET16_ASCAST): Define.
	(BPF_IMM32_ASCAST_K, BPF_IMM32_ASCAST_U): Likewise.
	(enum bpf_insn_id): Add BPF_INSN_ASCASTK and BPF_INSN_ASCASTU.

opcodes/
	* bpf-opc.c (bpf_opcodes): Add entries for addr_space_cast.

Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
 gas/testsuite/gas/bpf/ascast-pseudoc.d | 14 ++++++++++++++
 gas/testsuite/gas/bpf/ascast-pseudoc.s |  6 ++++++
 gas/testsuite/gas/bpf/ascast.d         | 14 ++++++++++++++
 gas/testsuite/gas/bpf/ascast.s         |  6 ++++++
 gas/testsuite/gas/bpf/bpf.exp          |  2 ++
 include/opcode/bpf.h                   | 12 ++++++++++++
 opcodes/bpf-opc.c                      |  8 ++++++++
 7 files changed, 62 insertions(+)
 create mode 100644 gas/testsuite/gas/bpf/ascast-pseudoc.d
 create mode 100644 gas/testsuite/gas/bpf/ascast-pseudoc.s
 create mode 100644 gas/testsuite/gas/bpf/ascast.d
 create mode 100644 gas/testsuite/gas/bpf/ascast.s
  

Comments

Jose E. Marchesi Sept. 30, 2026, 7:17 a.m. UTC | #1
Hi Vineet, thanks for the patch.

> gas doesn't support bpf arenas as it is missing addr_space_cast BPF
> instruction. It is essentially a `mov' with a distinguised offset and
> the two address spaces supported (0: generic, 1: arena) ecoded in the
> immediate.
>
> To keep implementation simple, spell out the two combinations as literal
> template text.  The entries have to precede `mov', whose mask covers only
> the opcode byte and would otherwise match first; movs8 and friends are
> already written this way.

Good approach!

I have a couple of comments below.

> The instruction is available at every ISA spec (just as clang).

What is the reason for that?

>
> gas/
> 	* testsuite/gas/bpf/ascast.s: New test.
> 	* testsuite/gas/bpf/ascast.d: New test.
> 	* testsuite/gas/bpf/ascast-pseudoc.s: New test.
> 	* testsuite/gas/bpf/ascast-pseudoc.d: New test.
> 	* testsuite/gas/bpf/bpf.exp: Run them.
>
> include/
> 	* opcode/bpf.h (BPF_OFFSET16_ASCAST): Define.
> 	(BPF_IMM32_ASCAST_K, BPF_IMM32_ASCAST_U): Likewise.
> 	(enum bpf_insn_id): Add BPF_INSN_ASCASTK and BPF_INSN_ASCASTU.
>
> opcodes/
> 	* bpf-opc.c (bpf_opcodes): Add entries for addr_space_cast.
>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
> ---
>  gas/testsuite/gas/bpf/ascast-pseudoc.d | 14 ++++++++++++++
>  gas/testsuite/gas/bpf/ascast-pseudoc.s |  6 ++++++
>  gas/testsuite/gas/bpf/ascast.d         | 14 ++++++++++++++
>  gas/testsuite/gas/bpf/ascast.s         |  6 ++++++
>  gas/testsuite/gas/bpf/bpf.exp          |  2 ++
>  include/opcode/bpf.h                   | 12 ++++++++++++
>  opcodes/bpf-opc.c                      |  8 ++++++++
>  7 files changed, 62 insertions(+)
>  create mode 100644 gas/testsuite/gas/bpf/ascast-pseudoc.d
>  create mode 100644 gas/testsuite/gas/bpf/ascast-pseudoc.s
>  create mode 100644 gas/testsuite/gas/bpf/ascast.d
>  create mode 100644 gas/testsuite/gas/bpf/ascast.s
>
> diff --git a/gas/testsuite/gas/bpf/ascast-pseudoc.d b/gas/testsuite/gas/bpf/ascast-pseudoc.d
> new file mode 100644
> index 000000000000..07cf304a4bd8
> --- /dev/null
> +++ b/gas/testsuite/gas/bpf/ascast-pseudoc.d
> @@ -0,0 +1,14 @@
> +#as: -EL -mdialect=pseudoc
> +#objdump: -dr -M dec,pseudoc
> +#source: ascast-pseudoc.s
> +#name: BPF address space cast instructions, pseudoc syntax
> +
> +.*: +file format .*bpf.*
> +
> +Disassembly of section .text:
> +
> +0+ <.text>:
> +   0:	bf 11 01 00 01 00 00 00 	r1 = addr_space_cast\(r1, 0, 1\)
> +   8:	bf 10 01 00 00 00 01 00 	r0 = addr_space_cast\(r1, 1, 0\)
> +  10:	bf 29 01 00 01 00 00 00 	r9 = addr_space_cast\(r2, 0, 1\)
> +  18:	bf 32 00 00 00 00 00 00 	r2=r3


I would add at least one test for big-endian, using some of the 0,1 and
1,0 address space combinations.

> diff --git a/gas/testsuite/gas/bpf/ascast-pseudoc.s b/gas/testsuite/gas/bpf/ascast-pseudoc.s
> new file mode 100644
> index 000000000000..0d0c35a278f8
> --- /dev/null
> +++ b/gas/testsuite/gas/bpf/ascast-pseudoc.s
> @@ -0,0 +1,6 @@
> +# Test for address space cast instructions
> +    .text
> +    r1 = addr_space_cast(r1, 0, 1)
> +    r0 = addr_space_cast(r1, 1, 0)
> +    r9 = addr_space_cast(r2, 0, 1)
> +    r2 = r3
> diff --git a/gas/testsuite/gas/bpf/ascast.d b/gas/testsuite/gas/bpf/ascast.d
> new file mode 100644
> index 000000000000..6069bbb1b00e
> --- /dev/null
> +++ b/gas/testsuite/gas/bpf/ascast.d
> @@ -0,0 +1,14 @@
> +#as: -EL -mdialect=normal
> +#objdump: -dr -M dec
> +#source: ascast.s
> +#name: BPF address space cast instructions, normal syntax
> +
> +.*: +file format .*bpf.*
> +
> +Disassembly of section .text:
> +
> +0+ <.text>:
> +   0:	bf 11 01 00 01 00 00 00 	addr_space_cast %r1,%r1,0,1
> +   8:	bf 10 01 00 00 00 01 00 	addr_space_cast %r0,%r1,1,0
> +  10:	bf 29 01 00 01 00 00 00 	addr_space_cast %r9,%r2,0,1
> +  18:	bf 32 00 00 00 00 00 00 	mov %r2,%r3
> diff --git a/gas/testsuite/gas/bpf/ascast.s b/gas/testsuite/gas/bpf/ascast.s
> new file mode 100644
> index 000000000000..1411f236b64a
> --- /dev/null
> +++ b/gas/testsuite/gas/bpf/ascast.s
> @@ -0,0 +1,6 @@
> +# Test for address space cast instructions
> +    .text
> +    addr_space_cast %r1, %r1, 0, 1
> +    addr_space_cast %r0, %r1, 1, 0
> +    addr_space_cast %r9, %r2, 0, 1
> +    mov %r2, %r3
> diff --git a/gas/testsuite/gas/bpf/bpf.exp b/gas/testsuite/gas/bpf/bpf.exp
> index 9884b36678c2..f58387b90705 100644
> --- a/gas/testsuite/gas/bpf/bpf.exp
> +++ b/gas/testsuite/gas/bpf/bpf.exp
> @@ -43,6 +43,8 @@ if {[istarget bpf*-*-*]} {
>      run_dump_test atomic-v1
>      run_dump_test atomic
>      run_dump_test atomic-pseudoc
> +    run_dump_test ascast
> +    run_dump_test ascast-pseudoc
>      run_dump_test indcall-1
>      run_dump_test indcall-1-pseudoc
>  
> diff --git a/include/opcode/bpf.h b/include/opcode/bpf.h
> index 1128c8307c5f..0ab542d5de03 100644
> --- a/include/opcode/bpf.h
> +++ b/include/opcode/bpf.h
> @@ -136,6 +136,15 @@ typedef uint64_t bpf_insn_word;
>  #define BPF_OFFSET16_MOVS8 ((uint64_t)8 << 32)
>  #define BPF_OFFSET16_MOVS16 ((uint64_t)16 << 32)
>  #define BPF_OFFSET16_MOVS32 ((uint64_t)32 << 32)
> +#define BPF_OFFSET16_ASCAST ((uint64_t)0x1 << 32)
> +
> +/* In an address space cast the immediate holds the destination address
> +   space in its upper half and the source address space in its lower
> +   half.  Only the generic address space 0 and the arena address space
> +   1 exist, so only these two combinations are meaningful.  */
> +
> +#define BPF_IMM32_ASCAST_K ((uint64_t)0x00000001) /* 0 <- 1.  */
> +#define BPF_IMM32_ASCAST_U ((uint64_t)0x00010000) /* 1 <- 0.  */
>  
>  #define BPF_IMM32_END16 ((uint64_t)0x00000010)
>  #define BPF_IMM32_END32 ((uint64_t)0x00000020)
> @@ -172,6 +181,9 @@ enum bpf_insn_id
>    BPF_INSN_NEGR, BPF_INSN_LSHR, BPF_INSN_LSHI,
>    BPF_INSN_RSHR, BPF_INSN_RSHI, BPF_INSN_ARSHR, BPF_INSN_ARSHI,
>    BPF_INSN_MOVS8R, BPF_INSN_MOVS16R, BPF_INSN_MOVS32R,
> +  /* Address space casts.  ASCASTK yields a pointer in the generic
> +     address space, ASCASTU one in the arena address space.  */

Are these instructions introducing a new class of instructions, would
you say?  If not, they should be listed aong with the ALU instructions.

> +  BPF_INSN_ASCASTK, BPF_INSN_ASCASTU,
>    BPF_INSN_MOVR, BPF_INSN_MOVI,
>    /* ALU32 instructions.  */
>    BPF_INSN_ADD32R, BPF_INSN_ADD32I, BPF_INSN_SUB32R, BPF_INSN_SUB32I,
> diff --git a/opcodes/bpf-opc.c b/opcodes/bpf-opc.c
> index 15fe5dfd1163..bbadd5e2c827 100644
> --- a/opcodes/bpf-opc.c
> +++ b/opcodes/bpf-opc.c
> @@ -93,6 +93,14 @@ const struct bpf_opcode bpf_opcodes[] =
>     BPF_V4, BPF_CODE|BPF_OFFSET16, BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_MOVS16},
>    {BPF_INSN_MOVS32R, "movs%W%dr , %sr , 32", "%dr%w=%w( s32 )%w%sr",
>     BPF_V4, BPF_CODE|BPF_OFFSET16, BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_MOVS32},
> +  {BPF_INSN_ASCASTK, "addr_space_cast%W%dr , %sr , 0 , 1",
> +   "%dr%w=%waddr_space_cast ( %sr ,%w0 ,%w1 )",
> +   BPF_V1, BPF_CODE|BPF_OFFSET16|BPF_IMM32,
> +   BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_ASCAST|BPF_IMM32_ASCAST_K},
> +  {BPF_INSN_ASCASTU, "addr_space_cast%W%dr , %sr , 1 , 0",
> +   "%dr%w=%waddr_space_cast ( %sr ,%w1 ,%w0 )",
> +   BPF_V1, BPF_CODE|BPF_OFFSET16|BPF_IMM32,
> +   BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_ASCAST|BPF_IMM32_ASCAST_U},

I would prefer to use a shorter name for the instruction in normal
syntax, preferably without underscores.  What about addrc (for "address
cast")?

>    {BPF_INSN_MOVR, "mov%W%dr , %sr", "%dr = %sr",
>     BPF_V1, BPF_CODE, BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X},
>    {BPF_INSN_MOVI, "mov%W%dr , %i32", "%dr = %i32",

Please include a patch for the manual as well, documenting the new
instruction.  I am thinking we might want to split the ALU instruction
in two different sections: "Arithmetic instructions" and "Data movement
instructions".  Right now all the ALU/ALU32 instructions are included in
the former, which is a bit funny as not all the ALU instructions do
arithmetic.

Thanks!
  
Vineet Gupta Oct. 8, 2026, 9:31 a.m. UTC | #2
Hi Jose,

Thanks for the review.

On 9/30/26 9:17 AM, Jose E. Marchesi wrote:
> Hi Vineet, thanks for the patch.
>> gas doesn't support bpf arenas as it is missing addr_space_cast BPF
>> instruction. It is essentially a `mov' with a distinguised offset and
>> the two address spaces supported (0: generic, 1: arena) ecoded in the
>> immediate.
>>
>> To keep implementation simple, spell out the two combinations as literal
>> template text.  The entries have to precede `mov', whose mask covers only
>> the opcode byte and would otherwise match first; movs8 and friends are
>> already written this way.
> Good approach!
>
> I have a couple of comments below.
>
>> The instruction is available at every ISA spec (just as clang).
> What is the reason for that?

As implemented today it is not tied to the ISA version. Verifier accepts 
addr_space_cast for any program that has arena associated with it.
clang behaves the same way. GCC gating it on something would thus be 
different, unnecessary and painful for no real gains.

>> +
>> +Disassembly of section .text:
>> +
>> +0+ <.text>:
>> +   0:	bf 11 01 00 01 00 00 00 	r1 = addr_space_cast\(r1, 0, 1\)
>> +   8:	bf 10 01 00 00 00 01 00 	r0 = addr_space_cast\(r1, 1, 0\)
>> +  10:	bf 29 01 00 01 00 00 00 	r9 = addr_space_cast\(r2, 0, 1\)
>> +  18:	bf 32 00 00 00 00 00 00 	r2=r3
>
> I would add at least one test for big-endian, using some of the 0,1 and
> 1,0 address space combinations.

Done.

>> diff --git a/include/opcode/bpf.h b/include/opcode/bpf.h
>> index 1128c8307c5f..0ab542d5de03 100644
>> --- a/include/opcode/bpf.h
>> +++ b/include/opcode/bpf.h
>> @@ -136,6 +136,15 @@ typedef uint64_t bpf_insn_word;
>>   #define BPF_OFFSET16_MOVS8 ((uint64_t)8 << 32)
>>   #define BPF_OFFSET16_MOVS16 ((uint64_t)16 << 32)
>>   #define BPF_OFFSET16_MOVS32 ((uint64_t)32 << 32)
>> +#define BPF_OFFSET16_ASCAST ((uint64_t)0x1 << 32)
>> +
>> +/* In an address space cast the immediate holds the destination address
>> +   space in its upper half and the source address space in its lower
>> +   half.  Only the generic address space 0 and the arena address space
>> +   1 exist, so only these two combinations are meaningful.  */
>> +
>> +#define BPF_IMM32_ASCAST_K ((uint64_t)0x00000001) /* 0 <- 1.  */
>> +#define BPF_IMM32_ASCAST_U ((uint64_t)0x00010000) /* 1 <- 0.  */
>>   
>>   #define BPF_IMM32_END16 ((uint64_t)0x00000010)
>>   #define BPF_IMM32_END32 ((uint64_t)0x00000020)
>> @@ -172,6 +181,9 @@ enum bpf_insn_id
>>     BPF_INSN_NEGR, BPF_INSN_LSHR, BPF_INSN_LSHI,
>>     BPF_INSN_RSHR, BPF_INSN_RSHI, BPF_INSN_ARSHR, BPF_INSN_ARSHI,
>>     BPF_INSN_MOVS8R, BPF_INSN_MOVS16R, BPF_INSN_MOVS32R,
>> +  /* Address space casts.  ASCASTK yields a pointer in the generic
>> +     address space, ASCASTU one in the arena address space.  */
> Are these instructions introducing a new class of instructions, would
> you say?  If not, they should be listed aong with the ALU instructions.

No, it is an ALU64 mov (BPF_ALU64|BPF_MOV|BPF_X) distinguished only by 
offset 1, same as how movs uses offsets 8, 16 and 32.
So it is kept here with rest of the *MOV?

>> +  BPF_INSN_ASCASTK, BPF_INSN_ASCASTU,
>>     BPF_INSN_MOVR, BPF_INSN_MOVI,
>>     /* ALU32 instructions.  */
>>     BPF_INSN_ADD32R, BPF_INSN_ADD32I, BPF_INSN_SUB32R, BPF_INSN_SUB32I,
>> diff --git a/opcodes/bpf-opc.c b/opcodes/bpf-opc.c
>> index 15fe5dfd1163..bbadd5e2c827 100644
>> --- a/opcodes/bpf-opc.c
>> +++ b/opcodes/bpf-opc.c
>> @@ -93,6 +93,14 @@ const struct bpf_opcode bpf_opcodes[] =
>>      BPF_V4, BPF_CODE|BPF_OFFSET16, BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_MOVS16},
>>     {BPF_INSN_MOVS32R, "movs%W%dr , %sr , 32", "%dr%w=%w( s32 )%w%sr",
>>      BPF_V4, BPF_CODE|BPF_OFFSET16, BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_MOVS32},
>> +  {BPF_INSN_ASCASTK, "addr_space_cast%W%dr , %sr , 0 , 1",
>> +   "%dr%w=%waddr_space_cast ( %sr ,%w0 ,%w1 )",
>> +   BPF_V1, BPF_CODE|BPF_OFFSET16|BPF_IMM32,
>> +   BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_ASCAST|BPF_IMM32_ASCAST_K},
>> +  {BPF_INSN_ASCASTU, "addr_space_cast%W%dr , %sr , 1 , 0",
>> +   "%dr%w=%waddr_space_cast ( %sr ,%w1 ,%w0 )",
>> +   BPF_V1, BPF_CODE|BPF_OFFSET16|BPF_IMM32,
>> +   BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_ASCAST|BPF_IMM32_ASCAST_U},
> I would prefer to use a shorter name for the instruction in normal
> syntax, preferably without underscores.  What about addrc (for "address
> cast")?

Done.

>>     {BPF_INSN_MOVR, "mov%W%dr , %sr", "%dr = %sr",
>>      BPF_V1, BPF_CODE, BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X},
>>     {BPF_INSN_MOVI, "mov%W%dr , %i32", "%dr = %i32",
> Please include a patch for the manual as well, documenting the new
> instruction.  I am thinking we might want to split the ALU instruction
> in two different sections: "Arithmetic instructions" and "Data movement
> instructions".  Right now all the ALU/ALU32 instructions are included in
> the former, which is a bit funny as not all the ALU instructions do
> arithmetic.

Done as well.

Thx,
-Vineet
  

Patch

diff --git a/gas/testsuite/gas/bpf/ascast-pseudoc.d b/gas/testsuite/gas/bpf/ascast-pseudoc.d
new file mode 100644
index 000000000000..07cf304a4bd8
--- /dev/null
+++ b/gas/testsuite/gas/bpf/ascast-pseudoc.d
@@ -0,0 +1,14 @@ 
+#as: -EL -mdialect=pseudoc
+#objdump: -dr -M dec,pseudoc
+#source: ascast-pseudoc.s
+#name: BPF address space cast instructions, pseudoc syntax
+
+.*: +file format .*bpf.*
+
+Disassembly of section .text:
+
+0+ <.text>:
+   0:	bf 11 01 00 01 00 00 00 	r1 = addr_space_cast\(r1, 0, 1\)
+   8:	bf 10 01 00 00 00 01 00 	r0 = addr_space_cast\(r1, 1, 0\)
+  10:	bf 29 01 00 01 00 00 00 	r9 = addr_space_cast\(r2, 0, 1\)
+  18:	bf 32 00 00 00 00 00 00 	r2=r3
diff --git a/gas/testsuite/gas/bpf/ascast-pseudoc.s b/gas/testsuite/gas/bpf/ascast-pseudoc.s
new file mode 100644
index 000000000000..0d0c35a278f8
--- /dev/null
+++ b/gas/testsuite/gas/bpf/ascast-pseudoc.s
@@ -0,0 +1,6 @@ 
+# Test for address space cast instructions
+    .text
+    r1 = addr_space_cast(r1, 0, 1)
+    r0 = addr_space_cast(r1, 1, 0)
+    r9 = addr_space_cast(r2, 0, 1)
+    r2 = r3
diff --git a/gas/testsuite/gas/bpf/ascast.d b/gas/testsuite/gas/bpf/ascast.d
new file mode 100644
index 000000000000..6069bbb1b00e
--- /dev/null
+++ b/gas/testsuite/gas/bpf/ascast.d
@@ -0,0 +1,14 @@ 
+#as: -EL -mdialect=normal
+#objdump: -dr -M dec
+#source: ascast.s
+#name: BPF address space cast instructions, normal syntax
+
+.*: +file format .*bpf.*
+
+Disassembly of section .text:
+
+0+ <.text>:
+   0:	bf 11 01 00 01 00 00 00 	addr_space_cast %r1,%r1,0,1
+   8:	bf 10 01 00 00 00 01 00 	addr_space_cast %r0,%r1,1,0
+  10:	bf 29 01 00 01 00 00 00 	addr_space_cast %r9,%r2,0,1
+  18:	bf 32 00 00 00 00 00 00 	mov %r2,%r3
diff --git a/gas/testsuite/gas/bpf/ascast.s b/gas/testsuite/gas/bpf/ascast.s
new file mode 100644
index 000000000000..1411f236b64a
--- /dev/null
+++ b/gas/testsuite/gas/bpf/ascast.s
@@ -0,0 +1,6 @@ 
+# Test for address space cast instructions
+    .text
+    addr_space_cast %r1, %r1, 0, 1
+    addr_space_cast %r0, %r1, 1, 0
+    addr_space_cast %r9, %r2, 0, 1
+    mov %r2, %r3
diff --git a/gas/testsuite/gas/bpf/bpf.exp b/gas/testsuite/gas/bpf/bpf.exp
index 9884b36678c2..f58387b90705 100644
--- a/gas/testsuite/gas/bpf/bpf.exp
+++ b/gas/testsuite/gas/bpf/bpf.exp
@@ -43,6 +43,8 @@  if {[istarget bpf*-*-*]} {
     run_dump_test atomic-v1
     run_dump_test atomic
     run_dump_test atomic-pseudoc
+    run_dump_test ascast
+    run_dump_test ascast-pseudoc
     run_dump_test indcall-1
     run_dump_test indcall-1-pseudoc
 
diff --git a/include/opcode/bpf.h b/include/opcode/bpf.h
index 1128c8307c5f..0ab542d5de03 100644
--- a/include/opcode/bpf.h
+++ b/include/opcode/bpf.h
@@ -136,6 +136,15 @@  typedef uint64_t bpf_insn_word;
 #define BPF_OFFSET16_MOVS8 ((uint64_t)8 << 32)
 #define BPF_OFFSET16_MOVS16 ((uint64_t)16 << 32)
 #define BPF_OFFSET16_MOVS32 ((uint64_t)32 << 32)
+#define BPF_OFFSET16_ASCAST ((uint64_t)0x1 << 32)
+
+/* In an address space cast the immediate holds the destination address
+   space in its upper half and the source address space in its lower
+   half.  Only the generic address space 0 and the arena address space
+   1 exist, so only these two combinations are meaningful.  */
+
+#define BPF_IMM32_ASCAST_K ((uint64_t)0x00000001) /* 0 <- 1.  */
+#define BPF_IMM32_ASCAST_U ((uint64_t)0x00010000) /* 1 <- 0.  */
 
 #define BPF_IMM32_END16 ((uint64_t)0x00000010)
 #define BPF_IMM32_END32 ((uint64_t)0x00000020)
@@ -172,6 +181,9 @@  enum bpf_insn_id
   BPF_INSN_NEGR, BPF_INSN_LSHR, BPF_INSN_LSHI,
   BPF_INSN_RSHR, BPF_INSN_RSHI, BPF_INSN_ARSHR, BPF_INSN_ARSHI,
   BPF_INSN_MOVS8R, BPF_INSN_MOVS16R, BPF_INSN_MOVS32R,
+  /* Address space casts.  ASCASTK yields a pointer in the generic
+     address space, ASCASTU one in the arena address space.  */
+  BPF_INSN_ASCASTK, BPF_INSN_ASCASTU,
   BPF_INSN_MOVR, BPF_INSN_MOVI,
   /* ALU32 instructions.  */
   BPF_INSN_ADD32R, BPF_INSN_ADD32I, BPF_INSN_SUB32R, BPF_INSN_SUB32I,
diff --git a/opcodes/bpf-opc.c b/opcodes/bpf-opc.c
index 15fe5dfd1163..bbadd5e2c827 100644
--- a/opcodes/bpf-opc.c
+++ b/opcodes/bpf-opc.c
@@ -93,6 +93,14 @@  const struct bpf_opcode bpf_opcodes[] =
    BPF_V4, BPF_CODE|BPF_OFFSET16, BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_MOVS16},
   {BPF_INSN_MOVS32R, "movs%W%dr , %sr , 32", "%dr%w=%w( s32 )%w%sr",
    BPF_V4, BPF_CODE|BPF_OFFSET16, BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_MOVS32},
+  {BPF_INSN_ASCASTK, "addr_space_cast%W%dr , %sr , 0 , 1",
+   "%dr%w=%waddr_space_cast ( %sr ,%w0 ,%w1 )",
+   BPF_V1, BPF_CODE|BPF_OFFSET16|BPF_IMM32,
+   BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_ASCAST|BPF_IMM32_ASCAST_K},
+  {BPF_INSN_ASCASTU, "addr_space_cast%W%dr , %sr , 1 , 0",
+   "%dr%w=%waddr_space_cast ( %sr ,%w1 ,%w0 )",
+   BPF_V1, BPF_CODE|BPF_OFFSET16|BPF_IMM32,
+   BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X|BPF_OFFSET16_ASCAST|BPF_IMM32_ASCAST_U},
   {BPF_INSN_MOVR, "mov%W%dr , %sr", "%dr = %sr",
    BPF_V1, BPF_CODE, BPF_CLASS_ALU64|BPF_CODE_MOV|BPF_SRC_X},
   {BPF_INSN_MOVI, "mov%W%dr , %i32", "%dr = %i32",