@@ -146,6 +146,10 @@ def llbuiltin(self, name):
146
146
llty = ll .FunctionType (ll .DoubleType (), [ll .DoubleType (), ll .IntType (32 )])
147
147
elif name == "llvm.copysign.f64" :
148
148
llty = ll .FunctionType (ll .DoubleType (), [ll .DoubleType (), ll .DoubleType ()])
149
+ elif name == "llvm.stacksave" :
150
+ llty = ll .FunctionType (ll .IntType (8 ).as_pointer (), [])
151
+ elif name == "llvm.stackrestore" :
152
+ llty = ll .FunctionType (ll .VoidType (), [ll .IntType (8 ).as_pointer ()])
149
153
elif name == self .target .print_function :
150
154
llty = ll .FunctionType (ll .VoidType (), [ll .IntType (8 ).as_pointer ()], var_arg = True )
151
155
elif name == "__artiq_personality" :
@@ -155,8 +159,10 @@ def llbuiltin(self, name):
155
159
elif name == "__artiq_reraise" :
156
160
llty = ll .FunctionType (ll .VoidType (), [])
157
161
elif name == "send_rpc" :
158
- llty = ll .FunctionType (ll .IntType ( 32 ), [ll .IntType (32 ), ll .IntType (8 ).as_pointer ()],
162
+ llty = ll .FunctionType (ll .VoidType ( ), [ll .IntType (32 ), ll .IntType (8 ).as_pointer ()],
159
163
var_arg = True )
164
+ elif name == "recv_rpc" :
165
+ llty = ll .FunctionType (ll .IntType (32 ), [ll .IntType (8 ).as_pointer ().as_pointer ()])
160
166
else :
161
167
assert False
162
168
@@ -559,12 +565,18 @@ def process_Closure(self, insn):
559
565
name = insn .name )
560
566
return llvalue
561
567
562
- # See session.c:send_rpc_value.
563
- def _rpc_tag (self , typ , root_type , root_loc ):
568
+ def _prepare_closure_call (self , insn ):
569
+ llclosure , llargs = self .map (insn .target_function ()), map (self .map , insn .arguments ())
570
+ llenv = self .llbuilder .extract_value (llclosure , 0 )
571
+ llfun = self .llbuilder .extract_value (llclosure , 1 )
572
+ return llfun , [llenv ] + list (llargs )
573
+
574
+ # See session.c:send_rpc_value and session.c:recv_rpc_value.
575
+ def _rpc_tag (self , typ , error_handler ):
564
576
if types .is_tuple (typ ):
565
577
assert len (typ .elts ) < 256
566
578
return b"t" + bytes ([len (typ .elts )]) + \
567
- b"" .join ([self ._rpc_tag (elt_type , root_type , root_loc )
579
+ b"" .join ([self ._rpc_tag (elt_type , error_handler )
568
580
for elt_type in typ .elts ])
569
581
elif builtins .is_none (typ ):
570
582
return b"n"
@@ -580,38 +592,53 @@ def _rpc_tag(self, typ, root_type, root_loc):
580
592
return b"s"
581
593
elif builtins .is_list (typ ):
582
594
return b"l" + self ._rpc_tag (builtins .get_iterable_elt (typ ),
583
- root_type , root_loc )
595
+ error_handler )
584
596
elif builtins .is_range (typ ):
585
597
return b"r" + self ._rpc_tag (builtins .get_iterable_elt (typ ),
586
- root_type , root_loc )
598
+ error_handler )
587
599
elif ir .is_option (typ ):
588
600
return b"o" + self ._rpc_tag (typ .params ["inner" ],
589
- root_type , root_loc )
601
+ error_handler )
590
602
else :
603
+ error_handler (typ )
604
+
605
+ def _build_rpc (self , fun_loc , fun_type , args , llnormalblock , llunwindblock ):
606
+ llservice = ll .Constant (ll .IntType (32 ), fun_type .service )
607
+
608
+ tag = b""
609
+
610
+ for arg in args :
611
+ def arg_error_handler (typ ):
612
+ printer = types .TypePrinter ()
613
+ note = diagnostic .Diagnostic ("note" ,
614
+ "value of type {type}" ,
615
+ {"type" : printer .name (typ )},
616
+ arg .loc )
617
+ diag = diagnostic .Diagnostic ("error" ,
618
+ "type {type} is not supported in remote procedure calls" ,
619
+ {"type" : printer .name (arg .typ )},
620
+ arg .loc )
621
+ self .engine .process (diag )
622
+ tag += self ._rpc_tag (arg .type , arg_error_handler )
623
+ tag += b":"
624
+
625
+ def ret_error_handler (typ ):
591
626
printer = types .TypePrinter ()
592
627
note = diagnostic .Diagnostic ("note" ,
593
628
"value of type {type}" ,
594
- {"type" : printer .name (root_type )},
595
- root_loc )
596
- diag = diagnostic .Diagnostic ("error" ,
597
- "type {type} is not supported in remote procedure calls" ,
598
629
{"type" : printer .name (typ )},
599
- root_loc )
630
+ fun_loc )
631
+ diag = diagnostic .Diagnostic ("error" ,
632
+ "return type {type} is not supported in remote procedure calls" ,
633
+ {"type" : printer .name (fun_type .ret )},
634
+ fun_loc )
600
635
self .engine .process (diag )
636
+ tag += self ._rpc_tag (fun_type .ret , ret_error_handler )
637
+ tag += b"\x00 "
601
638
602
- def _build_rpc (self , service , args , return_type ):
603
- llservice = ll .Constant (ll .IntType (32 ), service )
639
+ lltag = self .llconst_of_const (ir .Constant (tag + b"\x00 " , builtins .TStr ()))
604
640
605
- tag = b""
606
- for arg in args :
607
- if isinstance (arg , ir .Constant ):
608
- # Constants don't have locations, but conveniently
609
- # they also never fail to serialize.
610
- tag += self ._rpc_tag (arg .type , arg .type , None )
611
- else :
612
- tag += self ._rpc_tag (arg .type , arg .type , arg .loc )
613
- tag += b"\x00 "
614
- lltag = self .llconst_of_const (ir .Constant (tag , builtins .TStr ()))
641
+ llstackptr = self .llbuilder .call (self .llbuiltin ("llvm.stacksave" ), [])
615
642
616
643
llargs = []
617
644
for arg in args :
@@ -620,30 +647,79 @@ def _build_rpc(self, service, args, return_type):
620
647
self .llbuilder .store (llarg , llargslot )
621
648
llargs .append (llargslot )
622
649
623
- return self .llbuiltin ("send_rpc" ), [llservice , lltag ] + llargs
650
+ self .llbuilder .call (self .llbuiltin ("send_rpc" ),
651
+ [llservice , lltag ] + llargs )
652
+
653
+ # Don't waste stack space on saved arguments.
654
+ self .llbuilder .call (self .llbuiltin ("llvm.stackrestore" ), [llstackptr ])
655
+
656
+ # T result = {
657
+ # void *ptr = NULL;
658
+ # loop: int size = rpc_recv("tag", ptr);
659
+ # if(size) { ptr = alloca(size); goto loop; }
660
+ # else *(T*)ptr
661
+ # }
662
+ llprehead = self .llbuilder .basic_block
663
+ llhead = self .llbuilder .append_basic_block (name = llprehead .name + ".rpc.head" )
664
+ if llunwindblock :
665
+ llheadu = self .llbuilder .append_basic_block (name = llprehead .name + ".rpc.head.unwind" )
666
+ llalloc = self .llbuilder .append_basic_block (name = llprehead .name + ".rpc.alloc" )
667
+ lltail = self .llbuilder .append_basic_block (name = llprehead .name + ".rpc.tail" )
668
+
669
+ llslot = self .llbuilder .alloca (ll .IntType (8 ).as_pointer ())
670
+ self .llbuilder .store (ll .Constant (ll .IntType (8 ).as_pointer (), None ), llslot )
671
+ self .llbuilder .branch (llhead )
672
+
673
+ self .llbuilder .position_at_end (llhead )
674
+ if llunwindblock :
675
+ llsize = self .llbuilder .invoke (self .llbuiltin ("recv_rpc" ), [llslot ],
676
+ llheadu , llunwindblock )
677
+ self .llbuilder .position_at_end (llheadu )
678
+ else :
679
+ llsize = self .llbuilder .call (self .llbuiltin ("recv_rpc" ), [llslot ])
680
+ lldone = self .llbuilder .icmp_unsigned ('==' , llsize , ll .Constant (llsize .type , 0 ))
681
+ self .llbuilder .cbranch (lldone , lltail , llalloc )
682
+
683
+ self .llbuilder .position_at_end (llalloc )
684
+ llalloca = self .llbuilder .alloca (ll .IntType (8 ), llsize )
685
+ self .llbuilder .store (llalloca , llslot )
686
+ self .llbuilder .branch (llhead )
687
+
688
+ self .llbuilder .position_at_end (lltail )
689
+ llretty = self .llty_of_type (fun_type .ret , for_return = True )
690
+ llretptr = self .llbuilder .bitcast (llslot , llretty .as_pointer ())
691
+ llret = self .llbuilder .load (llretptr )
692
+ if not builtins .is_allocated (fun_type .ret ):
693
+ # We didn't allocate anything except the slot for the value itself.
694
+ # Don't waste stack space.
695
+ self .llbuilder .call (self .llbuiltin ("llvm.stackrestore" ), [llstackptr ])
696
+ if llnormalblock :
697
+ self .llbuilder .branch (llnormalblock )
698
+ return llret
624
699
625
- def prepare_call (self , insn ):
700
+ def process_Call (self , insn ):
626
701
if types .is_rpc_function (insn .target_function ().type ):
627
- return self ._build_rpc (insn .target_function ().type .service ,
702
+ return self ._build_rpc (insn .target_function ().loc ,
703
+ insn .target_function ().type ,
628
704
insn .arguments (),
629
- insn . target_function (). type . ret )
705
+ llnormalblock = None , llunwindblock = None )
630
706
else :
631
- llclosure , llargs = self .map (insn .target_function ()), map (self .map , insn .arguments ())
632
- llenv = self .llbuilder .extract_value (llclosure , 0 )
633
- llfun = self .llbuilder .extract_value (llclosure , 1 )
634
- return llfun , [llenv ] + list (llargs )
635
-
636
- def process_Call (self , insn ):
637
- llfun , llargs = self .prepare_call (insn )
638
- return self .llbuilder .call (llfun , llargs ,
639
- name = insn .name )
707
+ llfun , llargs = self ._prepare_closure_call (insn )
708
+ return self .llbuilder .call (llfun , llargs ,
709
+ name = insn .name )
640
710
641
711
def process_Invoke (self , insn ):
642
- llfun , llargs = self .prepare_call (insn )
643
712
llnormalblock = self .map (insn .normal_target ())
644
713
llunwindblock = self .map (insn .exception_target ())
645
- return self .llbuilder .invoke (llfun , llargs , llnormalblock , llunwindblock ,
646
- name = insn .name )
714
+ if types .is_rpc_function (insn .target_function ().type ):
715
+ return self ._build_rpc (insn .target_function ().loc ,
716
+ insn .target_function ().type ,
717
+ insn .arguments (),
718
+ llnormalblock , llunwindblock )
719
+ else :
720
+ llfun , llargs = self ._prepare_closure_call (insn )
721
+ return self .llbuilder .invoke (llfun , llargs , llnormalblock , llunwindblock ,
722
+ name = insn .name )
647
723
648
724
def process_Select (self , insn ):
649
725
return self .llbuilder .select (self .map (insn .condition ()),
0 commit comments