SegregateStackAccesses: rewrite around ExtraState

Move all per-function state and logic into a new SegregateFunctionStack
worker, so the pass class itself only holds module-wide state and the
runOnFunction body is just upgrade() + segregate(). The discovery loop
now uses the MFP ExtraState surface to capture the analysis value
before each interesting program point, replacing the splitBasicBlock
trick that introduced fake nodes only to read the lattice value at
them.

Other changes that ride along:
- StackOffsetCache memoizes getStackOffset per function.
- findAllWriters does a single sorted sweep instead of a quadratic
  scan over Ranges.
- The pipeline gains a stack-accesses-segregated artifact entry.
- Test fixtures (filecheck.ll, override.yml, segregate-stack-accesses
  configuration) are updated to match the new IR shape.
This commit is contained in:
Alessandro Di Federico
2026-05-27 14:28:14 +02:00
parent 5670527190
commit dc05030dd6
6 changed files with 1965 additions and 1128 deletions
File diff suppressed because it is too large Load Diff
+1
View File
@@ -26,6 +26,7 @@ USAGE: revng-artifact [options] <artifact> <binary>
emit-model-header - text/x.c+ptml
emit-type-definitions - text/x.c+tar+gz
cleanup-ir - application/x.llvm.bc+zstd
segregate-stack-accesses - application/x.llvm.bc+zstd
emit-c - text/x.c+ptml+tar+gz
emit-c-as-single-file - text/x.c+ptml
```
@@ -673,6 +673,11 @@ Branches:
- strip-dead-prototypes
- split-overflow-intrinsics
- dce
Artifacts:
Container: functions.bc.zstd
Kind: stack-accesses-segregated
SingleTargetFilename: clean-ir.ll
Docs: ""
- Name: emit-c
Pipes:
- Type: llvm-pipe
@@ -24,7 +24,7 @@ commands:
revng analyze --resume "$OUTPUT" detect-stack-size "$INPUT" -o /dev/null;
revng artifact --resume "$OUTPUT" make-segment-ref "$INPUT" |
revng artifact --resume "$OUTPUT" segregate-stack-accesses "$INPUT" |
revng opt -S | FileCheck ${SOURCE}.filecheck.ll;
revng artifact --resume "$OUTPUT" emit-c "$INPUT" -o /dev/null;
@@ -7,6 +7,10 @@ CHECK-DAG: add i64 [[IGN:.*]]%[[ARG1]]
CHECK-DAG: add i64 [[IGN:.*]]%[[ARG2]]
CHECK: }
CHECK: define i64 @local_call_raw_primitives_on_registers() [[IGN:.*]] {
CHECK-DAG: = call i64 @local_raw_primitives_on_registers(i64 2, i64 1)
CHECK: }
CHECK: define i64 @local_raw_pointers_on_registers(i64 %[[ARG1:.*]], i64 %[[ARG2:.*]]) [[IGN:.*]] {
CHECK-DAG: %[[ARG1_PTR:.*]] = inttoptr i64 %[[ARG1]] to ptr
CHECK-DAG: load i64, ptr %[[ARG1_PTR:.*]]
@@ -14,64 +18,147 @@ CHECK-DAG: %[[ARG2_PTR:.*]] = inttoptr i64 %[[ARG2]] to ptr
CHECK-DAG: load i64, ptr %[[ARG2_PTR]]
CHECK: }
CHECK: define i64 @local_call_raw_pointers_on_registers() [[IGN:.*]] {
CHECK-DAG: = call i64 @local_raw_pointers_on_registers(i64 [[ARG:.*]], i64 [[ARG]])
CHECK: }
CHECK: define i64 @local_raw_primitives_on_stack(i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[STACK_ARG:.*]]) [[IGN:.*]] {
CHECK-DAG: %[[STACK_ARG_AO:.*]] = call i64 @AddressOf([[IGN:.*]]i64 %[[STACK_ARG]])
CHECK-DAG: %[[STACK_ARG8:.*]] = add i64 %[[STACK_ARG_AO]], 8
CHECK-DAG: %[[STACK_ARG8:.*]] = add i64 %[[STACK_ARG]], 8
CHECK-DAG: %[[STACK_ARG8_PTR:.*]] = inttoptr i64 %[[STACK_ARG8]] to ptr
CHECK-DAG: load i64, ptr %[[STACK_ARG8_PTR:.*]]
CHECK-DAG: %[[STACK_ARG_PTR:.*]] = inttoptr i64 %[[STACK_ARG_AO]] to ptr
CHECK-DAG: %[[STACK_ARG_PTR:.*]] = inttoptr i64 %[[STACK_ARG]] to ptr
CHECK-DAG: load i64, ptr %[[STACK_ARG_PTR]]
CHECK: }
CHECK: define i64 @local_call_raw_primitives_on_stack() [[IGN:.*]] {
CHECK-DAG: %[[STACK:.*]] = alloca [16 x i8]
CHECK-DAG: %[[STACK_INT:.*]] = ptrtoint ptr %[[STACK]] to i64
CHECK-DAG: %[[STACK_INT_8:.*]] = add i64 %[[STACK_INT]], 8
CHECK-DAG: %[[STACK_8:.*]] = inttoptr i64 %[[STACK_INT_8]] to ptr
CHECK-DAG: store i64 8, ptr %[[STACK_8]]
CHECK-DAG: store i64 7, ptr %[[STACK]]
CHECK-DAG: = call i64 @local_raw_primitives_on_stack(i64 4, i64 3, i64 2, i64 1, i64 5, i64 6, i64 %[[STACK_INT]])
CHECK: }
CHECK: define i64 @local_cabi_primitives_on_registers(i64 %[[ARG1:.*]], i64 %[[ARG2:.*]]) [[IGN:.*]] {
CHECK-DAG: add i64 [[IGN:.*]]%[[ARG1]]
CHECK-DAG: add i64 [[IGN:.*]]%[[ARG2]]
CHECK: }
CHECK: define i64 @local_call_cabi_primitives_on_registers() [[IGN:.*]] {
CHECK-DAG: = call i64 @local_cabi_primitives_on_registers(i64 1, i64 2)
CHECK: }
CHECK: define i64 @local_cabi_primitives_on_stack(i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[STACK_ARG1:.*]], i64 %[[STACK_ARG2:.*]]) [[IGN:.*]] {
CHECK-DAG: %[[IGN:.*]] = add i64 %[[IGN:.*]]%[[STACK_ARG1]]
CHECK-DAG: %[[IGN:.*]] = add i64 %[[IGN:.*]]%[[STACK_ARG2]]
CHECK: }
CHECK: define i64 @local_call_cabi_primitives_on_stack() [[IGN:.*]] {
CHECK-DAG: = call i64 @local_cabi_primitives_on_stack(i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 [[SCALAR1:.*]], i64 [[SCALAR2:.*]])
CHECK: }
CHECK: define i64 @local_cabi_aggregate_on_registers(i64 %[[ARG1:.*]]) [[IGN:.*]] {
CHECK-DAG: %[[ARG1_AO:.*]] = call i64 @AddressOf([[IGN:.*]]i64 %[[ARG1]])
CHECK-DAG: %[[FIELD1_PTR:.*]] = inttoptr i64 %[[ARG1_AO]] to ptr
CHECK-DAG: %[[FIELD1_PTR:.*]] = inttoptr i64 %[[ARG1]] to ptr
CHECK-DAG: load i64, ptr %[[FIELD1_PTR]]
CHECK-DAG: %[[FIELD2_ADDR:.*]] = add i64 %[[ARG1_AO]], 8
CHECK-DAG: %[[FIELD2_ADDR:.*]] = add i64 %[[ARG1]], 8
CHECK-DAG: %[[FIELD2_PTR:.*]] = inttoptr i64 %[[FIELD2_ADDR]] to ptr
CHECK-DAG: load i64, ptr %[[FIELD2_PTR]], align 8
CHECK: }
CHECK: define i64 @local_call_cabi_aggregate_on_registers() [[IGN:.*]] {
CHECK-DAG: %[[STACK:.*]] = alloca [16 x i8]
CHECK-DAG: %[[STACK_INT:.*]] = ptrtoint ptr %[[STACK]] to i64
CHECK-DAG: store i64 1, ptr %[[STACK]]
CHECK-DAG: %[[STACK_INT_8:.*]] = add i64 %[[STACK_INT]], 8
CHECK-DAG: %[[STACK_8:.*]] = inttoptr i64 %[[STACK_INT_8]] to ptr
CHECK-DAG: store i64 2, ptr %[[STACK_8]]
CHECK-DAG: = call i64 @local_cabi_aggregate_on_registers(i64 %[[STACK_INT]])
CHECK: }
CHECK: define i64 @local_cabi_aggregate_on_stack(i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[STACK_ARG:.*]]) [[IGN:.*]] {
CHECK-DAG: %[[STACK_ARG_AO:.*]] = call i64 @AddressOf([[IGN:.*]]i64 %[[STACK_ARG]])
CHECK-DAG: %[[FIELD1_PTR:.*]] = inttoptr i64 %[[STACK_ARG_AO]] to ptr
CHECK-DAG: %[[FIELD1_PTR:.*]] = inttoptr i64 %[[STACK_ARG]] to ptr
CHECK-DAG: load i64, ptr %[[FIELD1_PTR]]
CHECK-DAG: %[[FIELD2_ADDR:.*]] = add i64 %[[STACK_ARG_AO]], 8
CHECK-DAG: %[[FIELD2_ADDR:.*]] = add i64 %[[STACK_ARG]], 8
CHECK-DAG: %[[FIELD2_PTR:.*]] = inttoptr i64 %[[FIELD2_ADDR]] to ptr
CHECK-DAG: load i64, ptr %[[FIELD2_PTR]]
CHECK: }
CHECK: define i64 @local_call_cabi_aggregate_on_stack() [[IGN:.*]] {
CHECK-DAG: %[[STACK:.*]] = alloca [16 x i8]
CHECK-DAG: %[[STACK_INT:.*]] = ptrtoint ptr %[[STACK]] to i64
CHECK-DAG: store i64 1, ptr %[[STACK]]
CHECK-DAG: %[[STACK_INT_8:.*]] = add i64 %[[STACK_INT]], 8
CHECK-DAG: %[[STACK_8:.*]] = inttoptr i64 %[[STACK_INT_8]] to ptr
CHECK-DAG: store i64 2, ptr %[[STACK_8]]
CHECK-DAG: = call i64 @local_cabi_aggregate_on_stack(i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 %[[STACK_INT]])
CHECK: }
CHECK: define i64 @local_cabi_aggregate_on_stack_and_registers(i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[IGN:.*]], i64 %[[STACK_ARG:.*]]) [[IGN:.*]] {
CHECK-DAG: %[[STACK_ARG_AO:.*]] = call i64 @AddressOf([[IGN:.*]]i64 %[[STACK_ARG]])
CHECK-DAG: %[[FIELD1_PTR:.*]] = inttoptr i64 %[[STACK_ARG_AO]] to ptr
CHECK-DAG: %[[FIELD1_PTR:.*]] = inttoptr i64 %[[STACK_ARG]] to ptr
CHECK-DAG: load i64, ptr %[[FIELD1_PTR]]
CHECK-DAG: %[[FIELD2_ADDR:.*]] = add i64 %[[STACK_ARG_AO]], 8
CHECK-DAG: %[[FIELD2_ADDR:.*]] = add i64 %[[STACK_ARG]], 8
CHECK-DAG: %[[FIELD2_PTR:.*]] = inttoptr i64 %[[FIELD2_ADDR]] to ptr
CHECK-DAG: load i64, ptr %[[FIELD2_PTR]]
CHECK: }
CHECK: define i64 @local_caller() [[IGN:.*]] {
CHECK-DAG: = call i64 @local_raw_primitives_on_registers(i64 2, i64 1)
CHECK-DAG: = call i64 @local_raw_pointers_on_registers(i64 %[[ARG:.*]], i64 %[[ARG]])
CHECK-DAG: %[[STACK:.*]] = call i64 @revng_call_stack_arguments([[IGN:.*]], i64 16)
CHECK-DAG: = call i64 @local_raw_primitives_on_stack(i64 4, i64 3, i64 2, i64 1, i64 5, i64 6, i64 %[[STACK]])
CHECK-DAG: = call i64 @local_cabi_primitives_on_registers(i64 1, i64 2)
TODO: devise a pipeline that highlights both arguments as immediates
CHECK-DAG: = call i64 @local_cabi_primitives_on_stack(i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 [[SCALAR1:.*]], i64 [[SCALAR2:.*]])
CHECK-DAG: %[[AGGREGATE:.*]] = call i64 @revng_call_stack_arguments([[IGN:.*]], i64 16)
CHECK-DAG: = call i64 @local_cabi_aggregate_on_registers(i64 %[[AGGREGATE]])
CHECK-DAG: %[[AGGREGATE:.*]] = call i64 @revng_call_stack_arguments([[IGN:.*]], i64 16)
CHECK-DAG: = call i64 @local_cabi_aggregate_on_stack(i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 %[[AGGREGATE]])
CHECK-DAG: %[[AGGREGATE:.*]] = call i64 @revng_call_stack_arguments([[IGN:.*]], i64 16)
CHECK-DAG: = call i64 @local_cabi_aggregate_on_stack_and_registers(i64 1, i64 2, i64 3, i64 4, i64 5, i64 %[[AGGREGATE]])
CHECK: define i64 @local_call_cabi_aggregate_on_stack_and_registers() [[IGN:.*]] {
CHECK-DAG: %[[STACK:.*]] = alloca [16 x i8]
CHECK-DAG: %[[STACK_INT:.*]] = ptrtoint ptr %[[STACK]] to i64
CHECK-DAG: store i64 1, ptr %[[STACK]]
CHECK-DAG: %[[STACK_INT_8:.*]] = add i64 %[[STACK_INT]], 8
CHECK-DAG: %[[STACK_8:.*]] = inttoptr i64 %[[STACK_INT_8]] to ptr
CHECK-DAG: store i64 2, ptr %[[STACK_8]]
CHECK-DAG: = call i64 @local_cabi_aggregate_on_stack_and_registers(i64 1, i64 2, i64 3, i64 4, i64 5, i64 %[[STACK_INT]])
CHECK: }
CHECK: define <{ i64, i64 }> @local_raw_return_small_aggregate() [[IGN:.*]] {
CHECK-DAG: %[[RESULT:.*]] = call <{ i64, i64 }> @struct_initializer(i64 124, i64 123)
CHECK-DAG: ret <{ i64, i64 }> %[[RESULT]]
CHECK: }
CHECK: define i64 @local_call_raw_return_small_aggregate() [[IGN:.*]] {
CHECK: %[[RESULT:.*]] = call <{ i64, i64 }> @local_raw_return_small_aggregate()
CHECK-DAG: call i64 @OpaqueExtractvalue(<{ i64, i64 }> %[[RESULT]], i64 1)
CHECK: }
CHECK: define [16 x i8] @local_cabi_return_small_aggregate() [[IGN:.*]] {
CHECK-DAG: %[[RETURN_ALLOCA:.*]] = alloca [16 x i8]
CHECK-DAG: %[[RETURN_ALLOCA_INT:.*]] = ptrtoint ptr %[[RETURN_ALLOCA]] to i64
CHECK-DAG: %[[RETURN_ALLOCA_INT_8:.*]] = add i64 %[[RETURN_ALLOCA_INT]], 8
CHECK-DAG: %[[RETURN_ALLOCA_8:.*]] = inttoptr i64 %[[RETURN_ALLOCA_INT_8]] to ptr
CHECK-DAG: store i64 124, ptr %[[RETURN_ALLOCA]]
CHECK-DAG: store i64 123, ptr %[[RETURN_ALLOCA_8]]
CHECK-DAG: %[[TO_RETURN:.*]] = load [16 x i8], ptr %[[RETURN_ALLOCA]]
CHECK-DAG: ret [16 x i8] %[[TO_RETURN]]
CHECK: }
CHECK: define i64 @local_call_cabi_return_small_aggregate() [[IGN:.*]] {
CHECK-DAG: %[[RETURN_ALLOCA:.*]] = alloca [16 x i8]
CHECK-DAG: %[[RETURN_ALLOCA_INT:.*]] = ptrtoint ptr %[[RETURN_ALLOCA]] to i64
CHECK-DAG: %[[RETURN_VALUE:.*]] = call [16 x i8] @local_cabi_return_small_aggregate()
CHECK-DAG: store [16 x i8] %[[RETURN_VALUE]], ptr %[[RETURN_ALLOCA]]
CHECK-DAG: %[[RETURN_ALLOCA_INT_8:.*]] = add i64 %[[RETURN_ALLOCA_INT]], 8
CHECK-DAG: %[[RETURN_ALLOCA_8:.*]] = inttoptr i64 %[[RETURN_ALLOCA_INT_8]] to ptr
CHECK-DAG: %[[TO_RETURN:.*]] = load i64, ptr %[[RETURN_ALLOCA_8]]
CHECK: }
CHECK: define [64 x i8] @local_cabi_return_big_aggregate() [[IGN:.*]] {
CHECK-DAG: %[[RETURN_ALLOCA:.*]] = alloca [64 x i8]
CHECK-DAG: %[[RETURN_ALLOCA_INT:.*]] = ptrtoint ptr %[[RETURN_ALLOCA]] to i64
CHECK-DAG: %[[RETURN_ALLOCA_INT_16:.*]] = add i64 %[[RETURN_ALLOCA_INT]], 16
CHECK-DAG: %[[RETURN_ALLOCA_16:.*]] = inttoptr i64 %[[RETURN_ALLOCA_INT_16]] to ptr
CHECK-DAG: store i64 123, ptr %[[RETURN_ALLOCA_16]]
CHECK-DAG: %[[TO_RETURN:.*]] = load [64 x i8], ptr %[[RETURN_ALLOCA]]
CHECK-DAG: ret [64 x i8] %[[TO_RETURN]]
CHECK: }
CHECK: define i64 @local_call_cabi_return_big_aggregate() [[IGN:.*]] {
CHECK-DAG: %[[RETURN_ALLOCA:.*]] = alloca [64 x i8]
CHECK-DAG: %[[RETURN_ALLOCA_INT:.*]] = ptrtoint ptr %[[RETURN_ALLOCA]] to i64
CHECK-DAG: %[[RETURN_VALUE:.*]] = call [64 x i8] @local_cabi_return_big_aggregate()
CHECK-DAG: store [64 x i8] %[[RETURN_VALUE]], ptr %[[RETURN_ALLOCA]]
CHECK-DAG: %[[RETURN_ALLOCA_INT_16:.*]] = add i64 %[[RETURN_ALLOCA_INT]], 16
CHECK-DAG: %[[RETURN_ALLOCA_16:.*]] = inttoptr i64 %[[RETURN_ALLOCA_INT_16]] to ptr
CHECK-DAG: %[[TO_RETURN:.*]] = load i64, ptr %[[RETURN_ALLOCA_16]]
CHECK: }
@@ -37,10 +37,18 @@ Functions:
Prototype:
Kind: DefinedType
Definition: "/TypeDefinitions/100008-CABIFunctionDefinition"
- Name: cabi_return_big_aggregate
- Name: cabi_return_small_aggregate
Prototype:
Kind: DefinedType
Definition: "/TypeDefinitions/100010-CABIFunctionDefinition"
- Name: cabi_return_big_aggregate
Prototype:
Kind: DefinedType
Definition: "/TypeDefinitions/100012-CABIFunctionDefinition"
- Name: raw_return_small_aggregate
Prototype:
Kind: DefinedType
Definition: "/TypeDefinitions/100013-RawFunctionDefinition"
TypeDefinitions:
- Kind: StructDefinition
ID: 100003
@@ -310,6 +318,28 @@ TypeDefinitions:
Definition: "/TypeDefinitions/100003-StructDefinition"
- Kind: StructDefinition
ID: 100009
Size: 16
Fields:
- Offset: 0
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 8
- Offset: 8
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 8
- Kind: CABIFunctionDefinition
ID: 100010
ABI: SystemV_x86_64
ReturnType:
Kind: DefinedType
Definition: "/TypeDefinitions/100009-StructDefinition"
Arguments: []
- Kind: StructDefinition
ID: 100011
Size: 64
Fields:
- Offset: 0
Type:
@@ -351,11 +381,24 @@ TypeDefinitions:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 8
Size: 64
- Kind: CABIFunctionDefinition
ID: 100010
ID: 100012
ABI: SystemV_x86_64
ReturnType:
Kind: DefinedType
Definition: "/TypeDefinitions/100009-StructDefinition"
Definition: "/TypeDefinitions/100011-StructDefinition"
Arguments: []
- Kind: RawFunctionDefinition
ID: 100013
Architecture: x86_64
ReturnValues:
- Location: rax_x86_64
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 8
- Location: rdx_x86_64
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 8