Blame SOURCES/rhbz1643997.0007-stapbpf-assembler-WIP-6-other-call-functions-s-print.patch

cab4ad
From 1d21523d10628fd14c4a16d5f85d98add9b5c0a6 Mon Sep 17 00:00:00 2001
cab4ad
From: Serhei Makarov <smakarov@redhat.com>
cab4ad
Date: Tue, 23 Oct 2018 13:35:08 -0400
cab4ad
Subject: [PATCH 07/32] stapbpf assembler WIP #6 :: other call functions
cab4ad
 ({s}printf and tapset)
cab4ad
cab4ad
Only very limited support for tapset functions (restricted to exit()
cab4ad
for now) due to the difficulty of resolving symbols after the semantic
cab4ad
pass is already completed. Could address this in the future.
cab4ad
cab4ad
* bpf_internal.h (program::use_tmp_space): check for overflow.
cab4ad
(globals::session): new field for systemtap_session (used by function lookup).
cab4ad
cab4ad
* bpf_translate.cxx (asm_stmt::has_jmp_target): new field.
cab4ad
(operator <<): printing rules for alloc, call.
cab4ad
(bpf_unparser::parse_asm_stmt): remove printf/error, BUGFIX alloc, call, string literal.
cab4ad
Also calculate has_jmp_target in the resulting stmt.
cab4ad
(bpf_unparser::visit_embeddedcode): handle printf, sprintf and exit().
cab4ad
Also fix the way fallthrough fields are populated to avoid spurious extra jump.
cab4ad
(bpf_unparser::emit_functioncall): new function. Factors out non-staptree code.
cab4ad
(bpf_unparser::visit_functioncall): use new emit_functioncall().
cab4ad
(print_format_add_tag): new function on std::string. Factors out string operations.
cab4ad
(bpf_unparser::emit_print_format): new function. Factors out non-staptree code.
cab4ad
(bpf_unparser::visit_print_format): use new emit_print_format().
cab4ad
(translate_bpf_pass): store session in globals.
cab4ad
---
cab4ad
 bpf-internal.h    |   5 +
cab4ad
 bpf-translate.cxx | 346 +++++++++++++++++++++++++++++++++++++++---------------
cab4ad
 2 files changed, 255 insertions(+), 96 deletions(-)
cab4ad
cab4ad
diff --git a/bpf-internal.h b/bpf-internal.h
cab4ad
index 61514db9f..3041bbdf5 100644
cab4ad
--- a/bpf-internal.h
cab4ad
+++ b/bpf-internal.h
cab4ad
@@ -30,6 +30,7 @@ namespace bpf {
cab4ad
 #define MAX_BPF_STACK 512
cab4ad
 #define BPF_REG_SIZE 8
cab4ad
 #define BPF_MAXSTRINGLEN 64
cab4ad
+// #define BPF_MAXSTRINGLEN 128 // TODO: Longer strings require storage allocator & better printf().
cab4ad
 #define BPF_MAXFORMATLEN 256
cab4ad
 #define BPF_MAXMAPENTRIES 2048
cab4ad
 // TODO: add BPF_MAXSPRINTFLEN
cab4ad
@@ -245,6 +246,7 @@ struct program
cab4ad
   {
cab4ad
     if (max_tmp_space < bytes)
cab4ad
       max_tmp_space = bytes;
cab4ad
+    assert(max_tmp_space <= MAX_BPF_STACK);
cab4ad
   }
cab4ad
 
cab4ad
   void mk_ld(insn_inserter &ins, int sz, value *dest, value *base, int off);
cab4ad
@@ -318,6 +320,9 @@ struct globals
cab4ad
     EXIT = 0,
cab4ad
     NUM_INTERNALS, // non-ABI
cab4ad
   };
cab4ad
+
cab4ad
+  // Used to resolve function symbols in embedded code.
cab4ad
+  systemtap_session *session;
cab4ad
 };
cab4ad
 } // namespace bpf
cab4ad
 
cab4ad
diff --git a/bpf-translate.cxx b/bpf-translate.cxx
cab4ad
index af3f54b50..df22401ad 100644
cab4ad
--- a/bpf-translate.cxx
cab4ad
+++ b/bpf-translate.cxx
cab4ad
@@ -135,8 +135,10 @@ has_side_effects (expression *e)
cab4ad
   return t.side_effects;
cab4ad
 }
cab4ad
 
cab4ad
-/* forward declaration */
cab4ad
+/* forward declarations */
cab4ad
 struct asm_stmt;
cab4ad
+static void print_format_add_tag(std::string&);
cab4ad
+static void print_format_add_tag(print_format*);
cab4ad
 
cab4ad
 struct bpf_unparser : public throwing_visitor
cab4ad
 {
cab4ad
@@ -238,6 +240,11 @@ struct bpf_unparser : public throwing_visitor
cab4ad
   value *emit_bool(expression *e);
cab4ad
   value *emit_context_var(bpf_context_vardecl *v);
cab4ad
 
cab4ad
+  value *emit_functioncall(functiondecl *f, const std::vector<value *> &args);
cab4ad
+  value *emit_print_format(const std::string &format,
cab4ad
+                           const std::vector<value *> &actual,
cab4ad
+                           bool print_to_stream = true);
cab4ad
+
cab4ad
   // Used for the embedded-code assembler:
cab4ad
   int64_t parse_imm (const asm_stmt &stmt, const std::string &str);
cab4ad
   size_t parse_asm_stmt (embeddedcode *s, size_t start,
cab4ad
@@ -594,24 +601,22 @@ bpf_unparser::visit_block (::block *s)
cab4ad
 
cab4ad
    <stmt> ::= label, <dest=label>;
cab4ad
    <stmt> ::= alloc, <dest=reg>, <imm=imm>;
cab4ad
-   <stmt> ::= call, <dest=reg>, <param[0]=function name>, <param[1]=arg>, ...;
cab4ad
-   <stmt> ::= printf, <param[0]=string constant>, <param[1]=arg>, ...;
cab4ad
-   <stmt> ::= error, <param[0]=string constant>, <param[1]=arg>, ...;
cab4ad
+   <stmt> ::= call, <dest=optreg>, <param[0]=function name>, <param[1]=arg>, ...;
cab4ad
    <stmt> ::= <code=integer opcode>, <dest=reg>, <src1=reg>,
cab4ad
               <off/jmp_target=off>, <imm=imm>;
cab4ad
 
cab4ad
    Supported argument types include:
cab4ad
 
cab4ad
-   <arg> ::= <reg> | <imm>
cab4ad
-   <reg> ::= <register index> | r<register index> |
cab4ad
-             $<identifier> | $<integer constant> | $$ | <string constant>
cab4ad
-   <imm> ::= <integer constant> | BPF_MAXSTRINGLEN
cab4ad
-   <off> ::= <imm> | <jump label>
cab4ad
+   <arg>    ::= <reg> | <imm>
cab4ad
+   <optreg> ::= <reg> | -
cab4ad
+   <reg>    ::= <register index> | r<register index> |
cab4ad
+                $<identifier> | $<integer constant> | $$ | <string constant>
cab4ad
+   <imm>    ::= <integer constant> | BPF_MAXSTRINGLEN | -
cab4ad
+   <off>    ::= <imm> | <jump label>
cab4ad
 
cab4ad
 */
cab4ad
 
cab4ad
-// TODO
cab4ad
-#define BPF_ASM_DEBUG
cab4ad
+// #define BPF_ASM_DEBUG
cab4ad
 
cab4ad
 struct asm_stmt {
cab4ad
   std::string kind;
cab4ad
@@ -621,6 +626,8 @@ struct asm_stmt {
cab4ad
   int64_t off, imm;
cab4ad
 
cab4ad
   // metadata for jmp instructions
cab4ad
+  // ??? The logic around these flags could be pruned a bit.
cab4ad
+  bool has_jmp_target = false;
cab4ad
   bool has_fallthrough = false;
cab4ad
   std::string jmp_target, fallthrough;
cab4ad
 
cab4ad
@@ -650,6 +657,19 @@ operator << (std::ostream& o, const asm_stmt& stmt)
cab4ad
         << stmt.imm << ";"
cab4ad
         << (stmt.has_fallthrough ? " +FALLTHROUGH " + stmt.fallthrough : "");
cab4ad
     }
cab4ad
+  else if (stmt.kind == "alloc")
cab4ad
+    {
cab4ad
+      o << "alloc, " << stmt.dest << ", " << stmt.imm << ";";
cab4ad
+    }
cab4ad
+  else if (stmt.kind == "call")
cab4ad
+    {
cab4ad
+      o << "call, " << stmt.dest << ", ";
cab4ad
+      for (unsigned k = 0; k < stmt.params.size(); k++)
cab4ad
+        {
cab4ad
+          o << stmt.params[k];
cab4ad
+          o << (k >= stmt.params.size() - 1 ? ";" : ", ");
cab4ad
+        }
cab4ad
+    }
cab4ad
   else
cab4ad
     o << "<unknown asm_stmt kind '" << stmt.kind << "'>";
cab4ad
   return o;
cab4ad
@@ -711,7 +731,7 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
cab4ad
   {
cab4ad
     char c = code[pos];
cab4ad
     char c2 = pos + 1 < n ? code [pos + 1] : 0;
cab4ad
-    if (isspace(c))
cab4ad
+    if (isspace(c) && !in_string)
cab4ad
       continue; // skip
cab4ad
     else if (in_comment)
cab4ad
       {
cab4ad
@@ -799,6 +819,10 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
cab4ad
         adjusted_loc.column++;
cab4ad
     }
cab4ad
 
cab4ad
+  // Now populate the statement data.
cab4ad
+
cab4ad
+  stmt = asm_stmt(); // clear pre-existing data
cab4ad
+
cab4ad
   // set token with adjusted source location
cab4ad
   stmt.tok = s->tok->adjust_location(adjusted_loc);
cab4ad
   adjusted_toks.push_back(stmt.tok);
cab4ad
@@ -830,14 +854,7 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
cab4ad
         throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (call expects at least 2 args, found %lu)", args.size()-1), stmt.tok);
cab4ad
       stmt.kind = args[0];
cab4ad
       stmt.dest = args[1];
cab4ad
-      for (unsigned k = 2; k < args.size(); k++)
cab4ad
-        stmt.params.push_back(args[k]);
cab4ad
-    }
cab4ad
-  else if (args[0] == "printf" || args[0] == "error")
cab4ad
-    {
cab4ad
-      if (args.size() < 2)
cab4ad
-        throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (%s expects at least 2 args, found %lu)", args[0].c_str(), args.size()-1), stmt.tok);
cab4ad
-      stmt.kind = args[0];
cab4ad
+      assert(stmt.params.empty());
cab4ad
       for (unsigned k = 2; k < args.size(); k++)
cab4ad
         stmt.params.push_back(args[k]);
cab4ad
     }
cab4ad
@@ -855,16 +872,16 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
cab4ad
       stmt.dest = args[1];
cab4ad
       stmt.src1 = args[2];
cab4ad
 
cab4ad
-      bool has_jmp_target =
cab4ad
+      stmt.has_jmp_target =
cab4ad
         BPF_CLASS(stmt.code) == BPF_JMP
cab4ad
         && BPF_OP(stmt.code) != BPF_EXIT
cab4ad
         && BPF_OP(stmt.code) != BPF_CALL;
cab4ad
       stmt.has_fallthrough = // only for jcond
cab4ad
-        has_jmp_target
cab4ad
+        stmt.has_jmp_target
cab4ad
         && BPF_OP(stmt.code) != BPF_JA;
cab4ad
       // XXX: stmt.fallthrough is computed by visit_embeddedcode
cab4ad
 
cab4ad
-      if (has_jmp_target)
cab4ad
+      if (stmt.has_jmp_target)
cab4ad
         {
cab4ad
           stmt.off = 0;
cab4ad
           stmt.jmp_target = args[3];
cab4ad
@@ -1168,6 +1185,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
cab4ad
 
cab4ad
       if (after_jump != NULL && stmt.kind == "label")
cab4ad
         {
cab4ad
+          after_jump->has_fallthrough = true;
cab4ad
           after_jump->fallthrough = stmt.dest;
cab4ad
         }
cab4ad
       else if (after_jump != NULL)
cab4ad
@@ -1183,6 +1201,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
cab4ad
           label_map[fallthrough_label] = b;
cab4ad
           set_block(b);
cab4ad
 
cab4ad
+          after_jump->has_fallthrough = true;
cab4ad
           after_jump->fallthrough = fallthrough_label;
cab4ad
         }
cab4ad
 
cab4ad
@@ -1205,6 +1224,12 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
cab4ad
           after_label = false;
cab4ad
           after_jump = &*it; // be sure to refer to original, not copied stmt
cab4ad
         }
cab4ad
+      else if (stmt.kind == "opcode" && BPF_CLASS(stmt.code) == BPF_JMP
cab4ad
+               && BPF_OP(stmt.code) != BPF_CALL /* CALL stays in the same block */)
cab4ad
+        {
cab4ad
+          after_label = false;
cab4ad
+          after_jump = &*it; // be sure to refer to original, not copied stmt
cab4ad
+        }
cab4ad
       else
cab4ad
         {
cab4ad
           after_label = false;
cab4ad
@@ -1216,7 +1241,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
cab4ad
                             "fallthrough on final asm_stmt"), stmt.tok);
cab4ad
 
cab4ad
   // emit statements
cab4ad
-  bool jumped_already = true;
cab4ad
+  bool jumped_already = false;
cab4ad
   set_block(entry_block);
cab4ad
   for (std::vector<asm_stmt>::iterator it = statements.begin();
cab4ad
        it != statements.end(); it++)
cab4ad
@@ -1234,24 +1259,27 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
cab4ad
       else if (stmt.kind == "alloc")
cab4ad
         {
cab4ad
           /* Reserve stack space and store its address in dest. */
cab4ad
-          int ofs = this_prog.max_tmp_space + stmt.imm;
cab4ad
-          value *dest = get_asm_reg(stmt, stmt.dest);
cab4ad
+          int ofs = -this_prog.max_tmp_space - stmt.imm;
cab4ad
           this_prog.use_tmp_space(-ofs);
cab4ad
+          // ??? Consider using a storage allocator and this_prog.new_obj().
cab4ad
+
cab4ad
+          value *dest = get_asm_reg(stmt, stmt.dest);
cab4ad
           this_prog.mk_binary(this_ins, BPF_ADD, dest,
cab4ad
                               this_prog.lookup_reg(BPF_REG_10) /*frame*/,
cab4ad
                               this_prog.new_imm(ofs));
cab4ad
         }
cab4ad
       else if (stmt.kind == "call")
cab4ad
         {
cab4ad
+          assert (!stmt.params.empty());
cab4ad
           std::string func_name = stmt.params[0];
cab4ad
           bpf_func_id hid = bpf_function_id(func_name);
cab4ad
           if (hid != __BPF_FUNC_MAX_ID)
cab4ad
             {
cab4ad
-              // TODO diagnostic: check if the number of arguments is correct
cab4ad
+              // ??? For diagnostics: check if the number of arguments is correct.
cab4ad
               regno r = BPF_REG_1; unsigned nargs = 0;
cab4ad
               for (unsigned k = 1; k < stmt.params.size(); k++)
cab4ad
                 {
cab4ad
-                  // ??? Could make params optional to avoid this part,
cab4ad
+                  // ??? Could make params optional to avoid the MOVs,
cab4ad
                   // ??? since the calling convention is well-known.
cab4ad
                   value *from_reg = emit_asm_arg(stmt, stmt.params[k]);
cab4ad
                   value *to_reg = this_prog.lookup_reg(r);
cab4ad
@@ -1259,30 +1287,117 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
cab4ad
                   nargs++; r++;
cab4ad
                 }
cab4ad
               this_prog.mk_call(this_ins, hid, nargs);
cab4ad
-              this_prog.mk_mov(this_ins, get_asm_reg(stmt, stmt.dest),
cab4ad
-                               this_prog.lookup_reg(BPF_REG_0) /* returnval */);
cab4ad
-              // ??? Could make stmt.dest optional to avoid this extra mov,
cab4ad
-              // ??? since the BPF_REG_0 convention is well-known.
cab4ad
+              if (stmt.dest != "-")
cab4ad
+                {
cab4ad
+                  value *dest = get_asm_reg(stmt, stmt.dest);
cab4ad
+                  this_prog.mk_mov(this_ins, dest,
cab4ad
+                                   this_prog.lookup_reg(BPF_REG_0) /* returnval */);
cab4ad
+                }
cab4ad
+              // ??? For diagnostics: check other cases with stmt.dest.
cab4ad
+            }
cab4ad
+          else if (func_name == "printf" || func_name == "sprintf")
cab4ad
+            {
cab4ad
+              if (stmt.params.size() < 2)
cab4ad
+                throw SEMANTIC_ERROR (_F("bpf embeddedcode '%s' expects format string, "
cab4ad
+                                         "none provided", func_name.c_str()),
cab4ad
+                                      stmt.tok);
cab4ad
+              std::string format = stmt.params[1];
cab4ad
+              if (format.size() < 2 || format[0] != '"'
cab4ad
+                  || format[format.size()-1] != '"')
cab4ad
+                throw SEMANTIC_ERROR (_F("bpf embeddedcode '%s' expects format string, "
cab4ad
+                                         "but first parameter is not a string literal",
cab4ad
+                                         func_name.c_str()), stmt.tok);
cab4ad
+              format = format.substr(1,format.size()-2); /* strip quotes */
cab4ad
+              format = translate_escapes(format);
cab4ad
+
cab4ad
+              bool print_to_stream = (func_name == "printf");
cab4ad
+              if (print_to_stream)
cab4ad
+                print_format_add_tag(format);
cab4ad
+
cab4ad
+              size_t format_bytes = format.size() + 1;
cab4ad
+              if (format_bytes > BPF_MAXFORMATLEN)
cab4ad
+                throw SEMANTIC_ERROR(_("Format string for print too long"), stmt.tok);
cab4ad
+
cab4ad
+              std::vector<value *> args;
cab4ad
+              for (unsigned k = 2; k < stmt.params.size(); k++)
cab4ad
+                args.push_back(emit_asm_arg(stmt, stmt.params[k]));
cab4ad
+              if (args.size() > 3)
cab4ad
+                throw SEMANTIC_ERROR(_NF("additional argument to print",
cab4ad
+                                         "too many arguments to print (%zu)",
cab4ad
+                                         args.size(), args.size()), stmt.tok);
cab4ad
+
cab4ad
+              value *retval = emit_print_format(format, args, print_to_stream);
cab4ad
+              if (retval != NULL && stmt.dest != "-")
cab4ad
+                {
cab4ad
+                  value *dest = get_asm_reg(stmt, stmt.dest);
cab4ad
+                  this_prog.mk_mov(this_ins, dest, retval);
cab4ad
+
cab4ad
+                }
cab4ad
+              // ??? For diagnostics: check other cases with retval and stmt.dest.
cab4ad
             }
cab4ad
           else
cab4ad
             {
cab4ad
-              // TODO function_name = params[0];
cab4ad
-              // TODO args = parse_reg(params[1]), parse_reg(params[2]), ...
cab4ad
-              // TODO emit_functioncall() with good bits from visit_functioncall()
cab4ad
-              throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode non-helper 'call' not yet supported"),
cab4ad
-                                    stmt.tok);
cab4ad
+              // TODO: Experimental code for supporting basic functioncalls.
cab4ad
+              // Needs improvement and simplification to work with full generality.
cab4ad
+              // But thus far, it is sufficient for calling exit().
cab4ad
+#if 1
cab4ad
+              if (func_name != "exit")
cab4ad
+                throw SEMANTIC_ERROR(_("BUG: bpf embeddedcode non-helper 'call' operation only supports printf(),sprintf(),exit() for now"), stmt.tok);
cab4ad
+#elif 1
cab4ad
+              throw SEMANTIC_ERROR(_("BUG: bpf embeddedcode non-helper 'call' operation only supports printf(),sprintf() for now"), stmt.tok);
cab4ad
+#endif
cab4ad
+#if 1
cab4ad
+              // ???: Passing systemtap_session through all the way to here
cab4ad
+              // seems intrusive, but less intrusive than moving
cab4ad
+              // embedded-code assembly to the translate_globals() pass.
cab4ad
+              symresolution_info sym (*glob.session);
cab4ad
+              functioncall *call = new functioncall;
cab4ad
+              call->tok = stmt.tok;
cab4ad
+              unsigned nargs = stmt.params.size() - 1;
cab4ad
+              std::vector<functiondecl*> fds
cab4ad
+                = sym.find_functions (call, func_name, nargs, stmt.tok);
cab4ad
+              delete call;
cab4ad
+
cab4ad
+              if (fds.empty())
cab4ad
+                // ??? Could call levenshtein_suggest() as in
cab4ad
+                // symresolution_info::visit_functioncall().
cab4ad
+                throw SEMANTIC_ERROR(_("bpf embeddedcode unresolved function call"), stmt.tok);
cab4ad
+              if (fds.size() > 1)
cab4ad
+                throw SEMANTIC_ERROR(_("bpf embeddedcode unhandled function overloading"), stmt.tok);
cab4ad
+              functiondecl *f = fds[0];
cab4ad
+              // TODO: Imitation of semantic_pass_symbols, does not
cab4ad
+              // cover full generality of the lookup process.
cab4ad
+              update_visitor_loop (*glob.session, glob.session->code_filters, f->body);
cab4ad
+              sym.current_function = f; sym.current_probe = 0;
cab4ad
+              f->body->visit (&sym);
cab4ad
+
cab4ad
+              // ??? For now, always inline the function call.
cab4ad
+              for (auto i = func_calls.begin(); i != func_calls.end(); ++i)
cab4ad
+                if (f == *i)
cab4ad
+                  throw SEMANTIC_ERROR (_("unhandled function recursion"), stmt.tok);
cab4ad
+
cab4ad
+              // Collect the function arguments.
cab4ad
+              std::vector<value *> args;
cab4ad
+              for (unsigned k = 1; k < stmt.params.size(); k++)
cab4ad
+                args.push_back(emit_asm_arg(stmt, stmt.params[k]));
cab4ad
+
cab4ad
+              if (args.size () != f->formal_args.size())
cab4ad
+                throw SEMANTIC_ERROR(_F("bpf embeddedcode call to function '%s' "
cab4ad
+                                        "expected %zu arguments, got %zu",
cab4ad
+                                        func_name.c_str(),
cab4ad
+                                        f->formal_args.size(), args.size()),
cab4ad
+                                     stmt.tok);
cab4ad
+
cab4ad
+              value *retval = emit_functioncall(f, args);
cab4ad
+              if (stmt.dest != "-")
cab4ad
+                {
cab4ad
+                  value *dest = get_asm_reg(stmt, stmt.dest);
cab4ad
+                  this_prog.mk_mov(this_ins, dest, retval);
cab4ad
+                }
cab4ad
+              // ??? For diagnostics: check other cases with retval and stmt.dest.
cab4ad
+#endif
cab4ad
             }
cab4ad
         }
cab4ad
-      else if (stmt.kind == "printf" || stmt.kind == "error")
cab4ad
-        {
cab4ad
-          // TODO Note that error() should be modeled on the tapset function in tapset/logging.stp
cab4ad
-          // TODO format = params[0];
cab4ad
-          // TODO args = parse_reg(params[1]), parse_reg(params[2]), ...
cab4ad
-          // TODO emit_print_format() with good bits from visit_print_format()
cab4ad
-          // TODO if (stmt.kind == "error") emit functioncall to exit() 
cab4ad
-          throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode 'printf/error' not yet supported"),
cab4ad
-                                stmt.tok);
cab4ad
-        }
cab4ad
       else if (stmt.kind == "opcode")
cab4ad
         {
cab4ad
           emit_asm_opcode (stmt, label_map);
cab4ad
@@ -1291,9 +1406,13 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
cab4ad
         throw SEMANTIC_ERROR (_F("BUG: bpf embeddedcode contains unexpected "
cab4ad
                                  "asm_stmt kind '%s'", stmt.kind.c_str()),
cab4ad
                               stmt.tok);
cab4ad
-      jumped_already = stmt.has_fallthrough;
cab4ad
       if (stmt.has_fallthrough)
cab4ad
-        set_block(label_map[stmt.fallthrough]);
cab4ad
+        {
cab4ad
+          jumped_already = true;
cab4ad
+          set_block(label_map[stmt.fallthrough]);
cab4ad
+        }
cab4ad
+      else
cab4ad
+        jumped_already = false;
cab4ad
     }
cab4ad
 
cab4ad
   // housekeeping -- deallocate adjusted_toks along with statements
cab4ad
@@ -1779,7 +1898,8 @@ bpf_unparser::visit_logical_and_expr (logical_and_expr* e)
cab4ad
   result = emit_bool (e);
cab4ad
 }
cab4ad
 
cab4ad
-// TODO: This matches translate.cxx, but it looks like the functionality is disabled in the parser.
cab4ad
+// ??? This matches the code in translate.cxx, but it looks like the
cab4ad
+// functionality has been disabled in the SystemTap parser.
cab4ad
 void
cab4ad
 bpf_unparser::visit_compound_expression (compound_expression* e)
cab4ad
 {
cab4ad
@@ -2573,30 +2693,17 @@ bpf_unparser::emit_str_arg(value *arg, int ofs, value *str)
cab4ad
   emit_mov(arg, out);
cab4ad
 }
cab4ad
 
cab4ad
-void
cab4ad
-bpf_unparser::visit_functioncall (functioncall *e)
cab4ad
+value *
cab4ad
+bpf_unparser::emit_functioncall (functiondecl *f, const std::vector<value *>& args)
cab4ad
 {
cab4ad
-  // ??? For now, always inline the function call.
cab4ad
-  // ??? Function overloading isn't handled.
cab4ad
-  if (e->referents.size () != 1)
cab4ad
-    throw SEMANTIC_ERROR (_("unhandled function overloading"), e->tok);
cab4ad
-  functiondecl *f = e->referents[0];
cab4ad
-
cab4ad
-  for (auto i = func_calls.begin(); i != func_calls.end(); ++i)
cab4ad
-    if (f == *i)
cab4ad
-      throw SEMANTIC_ERROR (_("unhandled function recursion"), e->tok);
cab4ad
-
cab4ad
-  assert (e->args.size () == f->formal_args.size ());
cab4ad
-
cab4ad
   // Create a new map for the function's local variables.
cab4ad
   locals_map *locals = new_locals(f->locals);
cab4ad
 
cab4ad
-  // Evaluate the function arguments and install in the map.
cab4ad
-  for (unsigned n = e->args.size (), i = 0; i < n; ++i)
cab4ad
+  // Install locals in the map.
cab4ad
+  unsigned n = args.size();
cab4ad
+  for (unsigned i = 0; i < n; ++i)
cab4ad
     {
cab4ad
-      value *r = this_prog.new_reg ();
cab4ad
-      emit_mov (r, emit_expr (e->args[i]));
cab4ad
-      const locals_map::value_type v (f->formal_args[i], r);
cab4ad
+      const locals_map::value_type v (f->formal_args[i], args[i]);
cab4ad
       auto ok = locals->insert (v);
cab4ad
       assert (ok.second);
cab4ad
     }
cab4ad
@@ -2622,7 +2729,47 @@ bpf_unparser::visit_functioncall (functioncall *e)
cab4ad
   this_locals = old_locals;
cab4ad
   delete locals;
cab4ad
 
cab4ad
-  result = retval;
cab4ad
+  return retval;
cab4ad
+}
cab4ad
+
cab4ad
+void
cab4ad
+bpf_unparser::visit_functioncall (functioncall *e)
cab4ad
+{
cab4ad
+  // ??? Function overloading isn't handled.
cab4ad
+  if (e->referents.size () != 1)
cab4ad
+    throw SEMANTIC_ERROR (_("unhandled function overloading"), e->tok);
cab4ad
+  functiondecl *f = e->referents[0];
cab4ad
+
cab4ad
+  // ??? For now, always inline the function call.
cab4ad
+  for (auto i = func_calls.begin(); i != func_calls.end(); ++i)
cab4ad
+    if (f == *i)
cab4ad
+      throw SEMANTIC_ERROR (_("unhandled function recursion"), e->tok);
cab4ad
+
cab4ad
+  // XXX: Should have been checked in earlier pass.
cab4ad
+  assert (e->args.size () == f->formal_args.size ());
cab4ad
+
cab4ad
+  // Evaluate and collect the function arguments.
cab4ad
+  std::vector<value *> args;
cab4ad
+  for (unsigned n = e->args.size (), i = 0; i < n; ++i)
cab4ad
+    {
cab4ad
+      value *r = this_prog.new_reg ();
cab4ad
+      emit_mov (r, emit_expr (e->args[i]));
cab4ad
+      args.push_back(r);
cab4ad
+    }
cab4ad
+
cab4ad
+  result = emit_functioncall(f, args);
cab4ad
+}
cab4ad
+
cab4ad
+static void
cab4ad
+print_format_add_tag(std::string& format)
cab4ad
+{
cab4ad
+  // surround the string with <MODNAME>...</MODNAME> to facilitate
cab4ad
+  // stapbpf recovering it from debugfs.
cab4ad
+  std::string start_tag = module_name;
cab4ad
+  start_tag = "<" + start_tag.erase(4,1) + ">";
cab4ad
+  std::string end_tag = start_tag + "\n";
cab4ad
+  end_tag.insert(1, "/");
cab4ad
+  format = start_tag + format + end_tag;
cab4ad
 }
cab4ad
 
cab4ad
 static void
cab4ad
@@ -2677,6 +2824,32 @@ print_format_add_tag(print_format *e)
cab4ad
     }
cab4ad
 }
cab4ad
 
cab4ad
+value *
cab4ad
+bpf_unparser::emit_print_format (const std::string& format,
cab4ad
+                                 const std::vector<value *>& actual,
cab4ad
+                                 bool print_to_stream)
cab4ad
+{
cab4ad
+  size_t nargs = actual.size();
cab4ad
+
cab4ad
+  // The bpf verifier requires that the format string be stored on the
cab4ad
+  // bpf program stack.  This is handled by bpf-opt.cxx lowering STR values.
cab4ad
+  size_t format_bytes = format.size() + 1;
cab4ad
+  this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1),
cab4ad
+                   this_prog.new_str(format));
cab4ad
+  emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes));
cab4ad
+  for (size_t i = 0; i < nargs; ++i)
cab4ad
+    emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]);
cab4ad
+
cab4ad
+  if (print_to_stream)
cab4ad
+    this_prog.mk_call(this_ins, BPF_FUNC_trace_printk, nargs + 2);
cab4ad
+  else
cab4ad
+    {
cab4ad
+      this_prog.mk_call(this_ins, BPF_FUNC_sprintf, nargs + 2);
cab4ad
+      return this_prog.lookup_reg(BPF_REG_0);
cab4ad
+    }
cab4ad
+  return NULL;
cab4ad
+}
cab4ad
+
cab4ad
 void
cab4ad
 bpf_unparser::visit_print_format (print_format *e)
cab4ad
 {
cab4ad
@@ -2696,9 +2869,9 @@ bpf_unparser::visit_print_format (print_format *e)
cab4ad
 			     "too many arguments to print (%zu)",
cab4ad
 			     e->args.size(), e->args.size()), e->tok);
cab4ad
 
cab4ad
-  value *actual[3] = { NULL, NULL, NULL };
cab4ad
+  std::vector<value *> actual;
cab4ad
   for (i = 0; i < nargs; ++i)
cab4ad
-    actual[i] = emit_expr(e->args[i]);
cab4ad
+    actual.push_back(emit_expr(e->args[i]));
cab4ad
 
cab4ad
   std::string format;
cab4ad
   if (e->print_with_format)
cab4ad
@@ -2750,36 +2923,17 @@ bpf_unparser::visit_print_format (print_format *e)
cab4ad
       if (e->print_with_newline)
cab4ad
 	format += '\n';
cab4ad
 
cab4ad
-      // surround the string with <MODNAME>...</MODNAME> to facilitate
cab4ad
-      // stapbpf recovering it from debugfs.
cab4ad
       if (e->print_to_stream)
cab4ad
-        {
cab4ad
-          std::string start_tag = module_name;
cab4ad
-          start_tag = "<" + start_tag.erase(4,1) + ">";
cab4ad
-          std::string end_tag = start_tag + "\n";
cab4ad
-          end_tag.insert(1, "/");
cab4ad
-          format = start_tag + format + end_tag;
cab4ad
-        }
cab4ad
+        print_format_add_tag(format);
cab4ad
     }
cab4ad
 
cab4ad
-  // The bpf verifier requires that the format string be stored on the
cab4ad
-  // bpf program stack.  This is handled by bpf-opt.cxx lowering STR values.
cab4ad
   size_t format_bytes = format.size() + 1;
cab4ad
   if (format_bytes > BPF_MAXFORMATLEN)
cab4ad
     throw SEMANTIC_ERROR(_("Format string for print too long"), e->tok);
cab4ad
-  this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1),
cab4ad
-                   this_prog.new_str(format));
cab4ad
-  emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes));
cab4ad
-  for (i = 0; i < nargs; ++i)
cab4ad
-    emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]);
cab4ad
 
cab4ad
-  if (e->print_to_stream)
cab4ad
-    this_prog.mk_call(this_ins, BPF_FUNC_trace_printk, nargs + 2);
cab4ad
-  else
cab4ad
-    {
cab4ad
-      this_prog.mk_call(this_ins, BPF_FUNC_sprintf, nargs + 2);
cab4ad
-      result = this_prog.lookup_reg(BPF_REG_0);
cab4ad
-    }
cab4ad
+  value *retval = emit_print_format(format, actual, e->print_to_stream);
cab4ad
+  if (retval != NULL)
cab4ad
+    result = retval;
cab4ad
 }
cab4ad
 
cab4ad
 // } // anon namespace
cab4ad
@@ -3409,7 +3563,7 @@ translate_bpf_pass (systemtap_session& s)
cab4ad
     return 1;
cab4ad
 
cab4ad
   BPF_Output eo(fd);
cab4ad
-  globals glob;
cab4ad
+  globals glob; glob.session = &s;
cab4ad
   int ret = 0;
cab4ad
   const token* t = 0;
cab4ad
   try
cab4ad
-- 
cab4ad
2.14.5
cab4ad