Closed RafaeNoor closed 2 years ago
It seems that the behavior you're seeing may be due to some issue with broadcast-impl
. I'm guessing this based on the following code that tries to replicate your example but gives the expected behavior with a stand-in implementation for broadcast-impl
:
#lang rosette
(require rosette/lib/synthax rosette/lib/destruct)
(struct reg (id) #:transparent #:mutable)
(struct BROADCAST (v0 size_o lane_size num_3 prec_i_o num_5 num_6 num_7) #:transparent)
(define broadcast-impl list)
(define (interpret prog env)
(destruct prog
[(reg id) (vector-ref-bv env id)]
[ (BROADCAST v0 size_o lane_size num_3 prec_i_o num_5 num_6 num_7)
(broadcast-impl (interpret v0 env) size_o lane_size
num_3 prec_i_o num_5
num_6 num_7)
]
)
)
(define-grammar (base_2988_grammar_operations_tree)
[expr
(choose
(reg (bv 0 4))
(
BROADCAST
(expr) ;; 128-bit Bitvector operand
256 ;; Integer Operand
256 ;; Lane Size
256 ;; Integer Operand
16 ;; Precision Operand
0 ;; Integer Operand
0 ;; Integer Operand
0 ;; Integer Operand
)
(BROADCAST (expr) ; <1 x i32>
512 512 512 32 0 0 0 )
)]
)
(define bounded-grammar
(base_2988_grammar_operations_tree #:depth 2 )
)
(define-symbolic sym-bv (bitvector 32))
(define env (vector sym-bv))
(define broadcast-expr
(BROADCAST (reg (bv 0 4)) ; <1 x i32>
512 512 512 32 0 0 0 )
)
;; Result takes sym-bv and concatenates
;; to itself 16 times
(define result (interpret broadcast-expr env))
(define sol?
(synthesize
#:forall (list env)
#:guarantee
(begin
(assert
(equal? result (interpret bounded-grammar env))
)
)
)
)
(displayln "Synthesizing Complete!")
(define satisfiable? (sat? sol?))
(define materialize
(if satisfiable?
(evaluate bounded-grammar sol?)
'()
)
)
(displayln "Synthesized solution")
(println materialize)
Hey Emina, I'll share the implementation of the method below:
(define (broadcast-impl a %vectsize %lanesize1 %lanesize2 %elemsize %laneoffset %arg0 %arg1 )
(define dst
(apply
concat
(for/list ([%outer.it (reverse (range 0 %vectsize %lanesize1))]) ; 0 256 256 => 1
(apply
concat
(for/list ([j0.new (reverse (range %laneoffset %lanesize2 %elemsize))]) ; 0 256 16 => 16
(define %lastidx1 (- %elemsize 1)) ; 15
(define %highidx0 (+ %lastidx1 %arg0)) ; 15 + 0 = 15
(define %1 (extract %highidx0 %arg0 a)) ; 15 0 => first 16 bits
%1
)
)
)
)
)
dot
)
Yes, the problem is in broadcast-impl
: it uses unsafe (unlifted) constructs, in this case for/list
and range
.
Specifically, range
will error if %vectsize and/or %lanesize1 are symbolic. You can add a printf
expression to this procedure and see that they are symbolic in your original implementation, because the two invocations of BROADCAST
get merged into a single value during the construction of bounded-grammar
. This happens because the BROADCAST
struct is transparent. And then, because range
errors unconditionally, there is no way to synthesize a solution that avoids the error.
When you introduce a second struct, the invocations are merged into a symbolic union instead, which gets destruct
ed by the interpreter, so the values remain concrete when they are passed to the range
function in broadcast-impl
. You can achieve this effect without a second struct, by adding the #:mutable
annotation to BROADCAST
.
The above solution is fragile though. It will work only if you can be sure that the arguments to range
will never get symbolic (i.e., the corresponding fields of BROADCAST
will always be concrete).
A good way to debug these issues is to use the error tracer.
I didn't realise that Rosette was treating the concrete parameters as symbolic values! In our use case, the parameters will always be constant values so I believe adding #:mutable
will do. Thank you for the answer. I'll definitely look into the error tracer tool too.
I did not know how to title this issue but I will explain the issue I am running into here.
I have implemented a DSL in Rosette using struct embeddings with an interpreter. I will show a minimal failing example here.
For example:
Note that the arguments to
BROADCAST
exceptv0
are integer parameters which affect howbroadcast-impl
functions. broadcast-impl takes an input bit vector and concatenates to itself (essentially broadcasting itself) depending on the specific values of the numeric parameters. Additionally, the bit vector length of the bit vector inputv0
can be different, and hence the output length of this result may also be different. However for a given parameterisation of the parameters the input length and output length will be the same.I want to use these structs and specific parameterisations to synthesise an equivalent function in this DSL. So I define the following grammar:
The synthesis query is issued as follows:
The synthesis outputs:
Given the construction of
broadcast-expr
, the grammar contains the clauses neccesary to synthesise the solution(BROADCAST (reg (bv #x0 4)) 512 512 512 32 0 0 0)
but it appears to be failing as there are two clauses forBROADCAST
but with different parameterisations (which are concrete).However when I do the following the synthesis works as expected:
Upon doing this, the synthesis works as expected and as such returns the desired solutions:
(BROADCAST (reg (bv #x0 4)) 512 512 512 32 0 0 0)
. Is there something wrong in the previous approach which prevented the synthesis from succeeding ? Ideally would there be a way to keep the different parameterisations in the same struct and provide their concrete values in the grammar clauses.Any help would be greatly appreciated!