[jit] Implement source code location support (API change)

David Malcolm dmalcolm@redhat.com
Tue Oct 15 01:17:00 GMT 2013


I've pushed the following to the "dmalcolm/jit" branch:
http://gcc.gnu.org/git/?p=gcc.git;a=commit;h=6cc326e5ad7c269a295fb6108e34b5c885a96b12

This patch implements gcc_jit_location, so that client code can mark
the various constructs with an underlying code location.  For example, a
JIT compiler for an interpreted scripting language may want to associate
the machine code with locations in the original script, allowing the
user to step through the JIT-compiled code in a debugger.

There is an API change in this patch:
gcc_jit_function_place_forward_label has gained a location as its
second parameter, and now has this signature:

 extern void
 gcc_jit_function_place_forward_label (gcc_jit_function *func,
				       gcc_jit_location *loc,
				       gcc_jit_label *lab);

An example of using this can be seen in jit.dg/test-fibonacci.c, where
the JIT code is set up to point the debugger at a comment within
that file containing C-like code.  It is possible to step through this
comment in the debugger:

  (gdb) break my_fibonacci
  (gdb) run
  Breakpoint 1, my_fibonacci (x=10) at testsuite/jit.dg/test-fibonacci.c:19
  19	FIRST_LINE + 3:   if (x < 2)
  (gdb) list
  14	0000000001111111111222222222233333333334444444444555555555566666666667
  15	1234567890123456789012345678901234567890123456789012345678901234567890
  16	FIRST_LINE + 0: int
  17	FIRST_LINE + 1: my_fibonacci (int x)
  18	FIRST_LINE + 2: {
  19	FIRST_LINE + 3:   if (x < 2)
  20	FIRST_LINE + 4:     return x;
  21	FIRST_LINE + 5:   else
  22	FIRST_LINE + 6:     return my_fibonacci (x - 1) + my_fibonacci (x - 2);
  23	FIRST_LINE + 7: }
  (gdb) next
  22	FIRST_LINE + 6:     return my_fibonacci (x - 1) + my_fibonacci (x - 2);
  (gdb) p x
  $1 = 10
  (gdb) step
  Breakpoint 1, my_fibonacci (x=9) at testsuite/jit.dg/test-fibonacci.c:19
  19	FIRST_LINE + 3:   if (x < 2)

File and line information appear to be correct, but looking at the .s
output, columns are zero, so there may be a bug in how I'm setting up
columns.

  my_fibonacci:
  .LFB0:
          .file 1 "testsuite/jit.dg/test-fibonacci.c"
          .loc 1 16 0
          .cfi_startproc
          pushq   %rbp
          .cfi_def_cfa_offset 16
          .cfi_offset 6, -16
          movq    %rsp, %rbp
          .cfi_def_cfa_register 6
          pushq   %rbx
          subq    $24, %rsp
          .cfi_offset 3, -24
          movl    %edi, -20(%rbp)
          .loc 1 19 0
          cmpl    $2, -20(%rbp)
          jge     .L2
          .loc 1 20 0
          movl    -20(%rbp), %eax
          jmp     .L4
  .L2:
          .loc 1 22 0
          movl    -20(%rbp), %eax
          subl    $1, %eax
          movl    %eax, %edi
          call    my_fibonacci@PLT

Implementation notes:

The linemap API appears to require source code locations to be created
in a very particular way, by creating references to files, and then,
within them, lines and columns, in the order the lines and columns
appear in the source file.

This is well-suited to a tokenizer, but for the JIT case we want to
allow the user to submit code without having to care about sorting
the source-code locations into order.

Hence internally we defer location creation: within the client's
code-creation hook, a gcc_jit_location represents a future location.
These are grouped by file and line, and a record is kept of associations
to be made between tree instances and gcc_jit_locations instances.
Upon return from the code-creation hook, all such locations are sorted,
and used in the linemap API to create source_location instances.
The various tree nodes then have their locations set to these instances.
---
 gcc/jit/ChangeLog.jit                      |  58 ++++++
 gcc/jit/internal-api.c                     | 300 ++++++++++++++++++++++++++---
 gcc/jit/internal-api.h                     | 100 +++++++++-
 gcc/jit/libgccjit.c                        |  13 +-
 gcc/jit/libgccjit.h                        |   8 +
 gcc/jit/libgccjit.map                      |   7 +-
 gcc/testsuite/ChangeLog.jit                |  10 +
 gcc/testsuite/jit.dg/test-factorial.c      |   4 +-
 gcc/testsuite/jit.dg/test-fibonacci.c      |  79 +++++---
 gcc/testsuite/jit.dg/test-sum-of-squares.c |   2 +-
 10 files changed, 516 insertions(+), 65 deletions(-)

diff --git a/gcc/jit/ChangeLog.jit b/gcc/jit/ChangeLog.jit
index cb6c2dd..5c06a72 100644
--- a/gcc/jit/ChangeLog.jit
+++ b/gcc/jit/ChangeLog.jit
@@ -1,3 +1,61 @@
+2013-10-14  David Malcolm  <dmalcolm@redhat.com>
+
+	* internal-api.c (gcc::jit::context::new_field): Implement
+	location support, by calling set_tree_location.
+	(gcc::jit::context::new_struct_type): Likewise.
+	(gcc::jit::context::new_param): Likewise.
+	(gcc::jit::context::new_function): Likewise.
+	(gcc::jit::context::new_global): Likewise.
+	(gcc::jit::context::new_local): Likewise.
+	(gcc::jit::context::new_binary_op): Likewise.
+	(gcc::jit::context::new_comparison): Likewise.
+	(gcc::jit::context::new_call): Likewise.
+	(gcc::jit::context::new_array_lookup): Likewise.
+	(gcc::jit::context::new_field_access): Likewise.
+	(gcc::jit::context::add_assignment): Likewise.
+	(gcc::jit::context::add_conditional): Likewise.
+	(gcc::jit::function::add_label): Likewise.
+	(gcc::jit::function::add_jump): Likewise.
+	(gcc::jit::function::add_return): Likewise.
+	(gcc::jit::function::place_forward_label): Likewise, adding
+	location parameter.
+	(gcc::jit::loop::loop): Add loc arg to place_forward_label.
+	(gcc::jit::loop::end): Likewise.
+	(gcc::jit::context::invoke_code_factory): Call handle_locations
+	after the client callback is done, before any GC can run.
+	(line_comparator): New.
+	(location_comparator): New.
+	(gcc::jit::context::handle_locations): New.
+	(gcc::jit::context::new_location): New.
+	(gcc::jit::context::set_tree_location): New.
+	(gcc::jit::context::get_source_file): New.
+	(gcc::jit::source_file::source_file): New.
+	(gcc::jit::source_file::get_source_line): New.
+	(gcc::jit::source_line::source_line): New.
+	(gcc::jit::source_line::get_location): New.
+	(gcc::jit::location::location): New.
+	* internal-api.h (gcc::jit::context::new_location): New.
+	(gcc::jit::context::set_tree_location): New.
+	(gcc::jit::context::handle_locations): New.
+	(gcc::jit::context::get_source_file): New.
+	(gcc::jit::context::m_source_files): New field.
+	(gcc::jit::context::m_cached_locations: New field.
+	(gcc::jit::function::place_forward_label): Add location
+	parameter.
+	(gcc::jit::function::set_tree_location): New.
+	(gcc::jit::source_file): New class.
+	(gcc::jit::source_line): New class.
+	(gcc::jit::location): New class.
+	* libgccjit.c (gcc_jit_context_new_location): New.
+	(gcc_jit_function_place_forward_label): Add location parameter,
+	changing public API.
+	* libgccjit.h (gcc_jit_context_new_location): New.
+	(gcc_jit_function_place_forward_label): Add location parameter,
+	changing public API.
+	* libgccjit.map (gcc_jit_context_new_location): New.
+	(main): Remove obsolete export.
+	(called_function): Likewise.
+
 2013-10-11  David Malcolm  <dmalcolm@redhat.com>
 
 	* internal-api.c: Update includes to reflect move of decl of
diff --git a/gcc/jit/internal-api.c b/gcc/jit/internal-api.c
index bec8544..158ea40 100644
--- a/gcc/jit/internal-api.c
+++ b/gcc/jit/internal-api.c
@@ -108,7 +108,6 @@ new_field (gcc::jit::location *loc,
 	   gcc::jit::type *type,
 	   const char *name)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (type);
   gcc_assert (name);
 
@@ -116,6 +115,9 @@ new_field (gcc::jit::location *loc,
   tree decl = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
 			  get_identifier (name), type->as_tree ());
 
+  if (loc)
+    set_tree_location (decl, loc);
+
   return new field (decl);
 }
 
@@ -126,7 +128,6 @@ new_struct_type (gcc::jit::location *loc,
 		 int num_fields,
 		 gcc::jit::field **fields)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (name);
   gcc_assert ((num_fields == 0) || fields);
 
@@ -148,6 +149,9 @@ new_struct_type (gcc::jit::location *loc,
 
   layout_type (t);
 
+  if (loc)
+    set_tree_location (t, loc);
+
   return new type (t);
 }
 
@@ -158,11 +162,13 @@ new_param (location *loc,
 	   type *type,
 	   const char *name)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (type);
   gcc_assert (name);
   tree inner = build_decl (UNKNOWN_LOCATION, PARM_DECL,
 			   get_identifier (name), type->as_tree ());
+  if (loc)
+    set_tree_location (inner, loc);
+
   return new param (inner);
 }
 
@@ -176,7 +182,6 @@ new_function (location *loc,
 	      param **params,
 	      int is_variadic)
  {
-  gcc_assert (NULL == loc);
   //can return_type be NULL?
   gcc_assert (name);
   gcc_assert ((num_params == 0) || params);
@@ -198,6 +203,9 @@ new_function (location *loc,
   /* FIXME: this uses input_location: */
   tree fndecl = build_fn_decl (name, fn_type);
 
+  if (loc)
+    set_tree_location (fndecl, loc);
+
   tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL,
 			     NULL_TREE, return_type->as_tree ());
   DECL_ARTIFICIAL (resdecl) = 1;
@@ -237,7 +245,6 @@ new_global (location *loc,
             type *type,
             const char *name)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (type);
   gcc_assert (name);
   tree inner = build_decl (UNKNOWN_LOCATION, VAR_DECL,
@@ -247,6 +254,9 @@ new_global (location *loc,
   DECL_COMMON (inner) = 1;
   DECL_EXTERNAL (inner) = 1;
 
+  if (loc)
+    set_tree_location (inner, loc);
+
   return new lvalue (inner);
 }
 
@@ -256,12 +266,13 @@ new_local (location *loc,
 	   type *type,
 	   const char *name)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (type);
   gcc_assert (name);
   tree inner = build_decl (UNKNOWN_LOCATION, VAR_DECL,
 			   get_identifier (name),
 			   type->as_tree ());
+  if (loc)
+    set_tree_location (inner, loc);
   return new local (inner);
 }
 
@@ -313,7 +324,6 @@ new_binary_op (location *loc,
   // FIXME: type-checking, or coercion?
   enum tree_code inner_op;
 
-  gcc_assert (NULL == loc);
   gcc_assert (result_type);
   gcc_assert (a);
   gcc_assert (b);
@@ -338,6 +348,9 @@ new_binary_op (location *loc,
 			    result_type->as_tree (),
 			    a->as_tree (),
 			    b->as_tree ());
+  if (loc)
+    set_tree_location (inner_expr, loc);
+
   return new rvalue (inner_expr);
 }
 
@@ -350,7 +363,6 @@ new_comparison (location *loc,
   // FIXME: type-checking, or coercion?
   enum tree_code inner_op;
 
-  gcc_assert (NULL == loc);
   gcc_assert (a);
   gcc_assert (b);
 
@@ -369,6 +381,8 @@ new_comparison (location *loc,
 			    boolean_type_node,
 			    a->as_tree (),
 			    b->as_tree ());
+  if (loc)
+    set_tree_location (inner_expr, loc);
   return new rvalue (inner_expr);
 }
 
@@ -381,7 +395,6 @@ new_call (location *loc,
   tree fndecl;
   vec<tree, va_gc> *tree_args;
 
-  gcc_assert (NULL == loc);
   gcc_assert (func);
   gcc_assert (numargs >= 0);
   gcc_assert ((args == 0) || (args != NULL));
@@ -398,6 +411,9 @@ new_call (location *loc,
   tree fntype = TREE_TYPE (fndecl);
   tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl);
 
+  if (loc)
+    set_tree_location (fn, loc);
+
   return new rvalue (build_call_vec (func->get_return_type_as_tree (),
 				     fn, tree_args));
 
@@ -418,7 +434,6 @@ new_array_lookup (location *loc,
 		  rvalue *ptr,
 		  rvalue *index)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (ptr);
   gcc_assert (index);
 
@@ -435,6 +450,8 @@ new_array_lookup (location *loc,
     {
       tree t_result = build4 (ARRAY_REF, t_type_star_ptr, t_ptr, t_index,
 			      NULL_TREE, NULL_TREE);
+      if (loc)
+        set_tree_location (t_result, loc);
       return new rvalue (t_result);
     }
   else
@@ -448,6 +465,14 @@ new_array_lookup (location *loc,
       tree t_address = build2 (POINTER_PLUS_EXPR, t_type_ptr, t_ptr, t_offset);
 
       tree t_indirection = build1 (INDIRECT_REF, t_type_star_ptr, t_address);
+      if (loc)
+        {
+          set_tree_location (t_sizeof, loc);
+          set_tree_location (t_offset, loc);
+          set_tree_location (t_address, loc);
+          set_tree_location (t_indirection, loc);
+        }
+
       return new rvalue (t_indirection);
     }
 }
@@ -467,7 +492,6 @@ new_field_access (location *loc,
 		  rvalue *ptr_or_struct,
 		  const char *fieldname)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (ptr_or_struct);
   gcc_assert (fieldname);
 
@@ -479,6 +503,8 @@ new_field_access (location *loc,
   if (TREE_CODE (type) == POINTER_TYPE)
     {
       datum = build1 (INDIRECT_REF, type, datum);
+      if (loc)
+        set_tree_location (datum, loc);
       type = TREE_TYPE (type);
     }
 
@@ -491,6 +517,8 @@ new_field_access (location *loc,
     }
   tree ref = build3 (COMPONENT_REF, TREE_TYPE (field), datum,
 		     field, NULL_TREE);
+  if (loc)
+    set_tree_location (ref, loc);
   return new lvalue (ref);
 }
 
@@ -603,7 +631,6 @@ add_assignment (location *loc,
 		lvalue *lvalue,
 		rvalue *rvalue)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (lvalue);
   gcc_assert (rvalue);
   gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
@@ -611,13 +638,19 @@ add_assignment (location *loc,
   tree t_lvalue = lvalue->as_tree ();
   tree t_rvalue = rvalue->as_tree ();
   if (TREE_TYPE (t_rvalue) != TREE_TYPE (t_lvalue))
-    t_rvalue = build1 (CONVERT_EXPR,
-		       TREE_TYPE (t_lvalue),
-		       t_rvalue);
+    {
+      t_rvalue = build1 (CONVERT_EXPR,
+		         TREE_TYPE (t_lvalue),
+		         t_rvalue);
+      if (loc)
+	set_tree_location (t_rvalue, loc);
+    }
 
   tree stmt =
     build2 (MODIFY_EXPR, TREE_TYPE (t_lvalue),
 	    t_lvalue, t_rvalue);
+  if (loc)
+    set_tree_location (stmt, loc);
   tsi_link_after (&m_stmt_iter, stmt, TSI_CONTINUE_LINKING);
 }
 
@@ -628,7 +661,6 @@ add_conditional (location *loc,
 		 label *on_true,
 		 label *on_false)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (boolval);
   gcc_assert (on_true);
   /* on_false can be NULL */
@@ -639,16 +671,24 @@ add_conditional (location *loc,
      Shim it by creating jumps to the labels */
   tree true_jump = build1 (GOTO_EXPR, void_type_node,
 			   on_true->as_label_decl ());
+  if (loc)
+    set_tree_location (true_jump, loc);
   tree false_jump;
   if (on_false)
-    false_jump = build1 (GOTO_EXPR, void_type_node,
-			 on_false->as_label_decl ());
+    {
+      false_jump = build1 (GOTO_EXPR, void_type_node,
+			   on_false->as_label_decl ());
+      if (loc)
+        set_tree_location (false_jump, loc);
+    }
   else
     false_jump = NULL;
 
   tree stmt =
     build3 (COND_EXPR, void_type_node, boolval->as_tree (),
 	    true_jump, false_jump);
+  if (loc)
+    set_tree_location (stmt, loc);
   tsi_link_after (&m_stmt_iter, stmt, TSI_CONTINUE_LINKING);
 }
 
@@ -657,17 +697,16 @@ gcc::jit::function::
 add_label (location *loc,
 	   const char *name)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
 
   label *lab = new label (this, name);
-  place_forward_label (lab);
+  place_forward_label (loc, lab);
   return lab;
 }
 
 void
 gcc::jit::function::
-place_forward_label (label *lab)
+place_forward_label (location *loc, label *lab)
 {
   gcc_assert (lab);
   gcc_assert (NULL == lab->m_label_expr); // must not have already been placed
@@ -676,6 +715,8 @@ place_forward_label (label *lab)
   lab->m_label_expr = build1 (LABEL_EXPR,
 			     void_type_node,
 			     lab->as_label_decl ());
+  if (loc)
+    set_tree_location (lab->m_label_expr, loc);
   tsi_link_after (&m_stmt_iter, lab->m_label_expr, TSI_CONTINUE_LINKING);
 }
 
@@ -684,7 +725,6 @@ gcc::jit::function::
 add_jump (location *loc,
 	  label *target)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (target);
   gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
 
@@ -695,6 +735,8 @@ add_jump (location *loc,
   //tree stmt = build_and_jump (&LABEL_EXPR_LABEL (target->label_));
   TREE_USED (target->as_label_decl ()) = 1;
   tree stmt = build1 (GOTO_EXPR, void_type_node, target->as_label_decl ());
+  if (loc)
+    set_tree_location (stmt, loc);
   tsi_link_after (&m_stmt_iter, stmt, TSI_CONTINUE_LINKING);
 
   /*
@@ -721,7 +763,6 @@ gcc::jit::function::
 add_return (location *loc,
 	    rvalue *rvalue)
 {
-  gcc_assert (NULL == loc);
   gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
 
   tree return_type = TREE_TYPE (TREE_TYPE (m_inner_fndecl));
@@ -735,6 +776,11 @@ add_return (location *loc,
 			       t_lvalue, t_rvalue);
   tree return_stmt = build1 (RETURN_EXPR, return_type,
 			     modify_retval);
+  if (loc)
+    {
+      set_tree_location (modify_retval, loc);
+      set_tree_location (return_stmt, loc);
+    }
   tsi_link_after (&m_stmt_iter, return_stmt, TSI_CONTINUE_LINKING);
 }
 
@@ -773,7 +819,7 @@ loop (function *func, location *loc, rvalue *boolval) :
   m_label_body = func->new_forward_label ("loop_body");
   m_label_end = func->new_forward_label ("loop_end");
   func->add_conditional (loc, boolval, m_label_body, m_label_end);
-  func->place_forward_label (m_label_body);
+  func->place_forward_label (loc, m_label_body);
 }
 
 void
@@ -781,7 +827,7 @@ gcc::jit::loop::
 end (location *loc)
 {
   m_func->add_jump (loc, m_label_cond);
-  m_func->place_forward_label (m_label_end);
+  m_func->place_forward_label (loc, m_label_end);
 }
 
 void
@@ -1030,6 +1076,11 @@ invoke_code_factory ()
     {
       int i;
       function *func;
+
+      /* No GC can happen yet; process the cached source locations.  */
+      handle_locations ();
+
+      /* Postprocess the functions.  This could trigger GC.  */
       FOR_EACH_VEC_ELT (m_functions, i, func)
 	{
 	  gcc_assert (func);
@@ -1038,6 +1089,102 @@ invoke_code_factory ()
     }
 }
 
+static int
+line_comparator (const void *lhs, const void *rhs)
+{
+  const gcc::jit::source_line *line_lhs = \
+    *static_cast<const gcc::jit::source_line * const*> (lhs);
+  const gcc::jit::source_line *line_rhs = \
+    *static_cast<const gcc::jit::source_line * const*> (rhs);
+  return line_lhs->get_line_num () - line_rhs->get_line_num ();
+}
+
+static int
+location_comparator (const void *lhs, const void *rhs)
+{
+  const gcc::jit::location *loc_lhs = \
+    *static_cast<const gcc::jit::location * const *> (lhs);
+  const gcc::jit::location *loc_rhs = \
+    *static_cast<const gcc::jit::location * const *> (rhs);
+  return loc_lhs->get_column_num () - loc_rhs->get_column_num ();
+}
+
+void
+gcc::jit::context::
+handle_locations ()
+{
+  /* Create the source code locations, following the ordering rules
+     imposed by the linemap API.
+
+     line_table is a global.  */
+  int i;
+  source_file *file;
+
+  FOR_EACH_VEC_ELT (m_source_files, i, file)
+    {
+      linemap_add (line_table, LC_ENTER, false, file->get_filename (), 0);
+
+      /* Sort lines by ascending line numbers.  */
+      file->m_source_lines.qsort (&line_comparator);
+
+      int j;
+      source_line *line;
+      FOR_EACH_VEC_ELT (file->m_source_lines, j, line)
+	{
+	  int k;
+	  location *loc;
+
+	  /* Sort locations in line by ascending column numbers.  */
+	  line->m_locations.qsort (&location_comparator);
+
+	  /* Determine maximum column within this line.  */
+	  gcc_assert (line->m_locations.length () > 0);
+	  location *final_column =
+	    line->m_locations[line->m_locations.length () - 1];
+	  int max_col = final_column->get_column_num ();
+
+	  linemap_line_start (line_table, line->get_line_num (), max_col);
+	  FOR_EACH_VEC_ELT (line->m_locations, k, loc)
+	    {
+	      loc->m_srcloc =					   \
+		linemap_position_for_column (line_table, loc->get_column_num ());
+	    }
+	}
+
+      linemap_add (line_table, LC_LEAVE, false, NULL, 0);
+    }
+
+  /* Now assign them to tree nodes as appropriate.  */
+  std::pair<tree, location *> *cached_location;
+
+  FOR_EACH_VEC_ELT (m_cached_locations, i, cached_location)
+    {
+      tree t = cached_location->first;
+      source_location srcloc = cached_location->second->m_srcloc;
+#if 0
+      inform (srcloc, "location of ");
+      debug_tree (t);
+#endif
+
+      /* This covers expressions: */
+      if (CAN_HAVE_LOCATION_P (t))
+	SET_EXPR_LOCATION (t, srcloc);
+      else if (DECL_MINIMAL_CHECK (t))
+	DECL_SOURCE_LOCATION (t) = srcloc;
+      else
+	{
+	  /* Don't know how to set location on this node.  */
+	  if (0)
+	    {
+	      fprintf (stderr, "can't set location on:");
+	      debug_tree (t);
+	      fprintf (stderr, "\n");
+	    }
+	}
+    }
+}
+
+
 void
 gcc::jit::context::
 add_error (const char */*msg*/)
@@ -1075,3 +1222,106 @@ get_code (const char *funcname)
 
   return code;
 }
+
+/* Dealing with the linemap API.  */
+
+gcc::jit::location *
+gcc::jit::context::
+new_location (const char *filename,
+	      int line,
+	      int column)
+{
+  /* Get the source_file for filename, creating if necessary.  */
+  source_file *src_file = get_source_file (filename);
+  /* Likewise for the line within the file.  */
+  source_line *src_line = src_file->get_source_line (line);
+  /* Likewise for the column within the line.  */
+  location *loc = src_line->get_location (column);
+  return loc;
+}
+
+void
+gcc::jit::context::
+set_tree_location (tree t, location *loc)
+{
+  m_cached_locations.safe_push (std::make_pair (t, loc));
+}
+
+
+gcc::jit::source_file *
+gcc::jit::context::
+get_source_file (const char *filename)
+{
+  /* Locate the file.
+     For simplicitly, this is currently a linear search.
+     Replace with a hash if this shows up in the profile.  */
+  int i;
+  source_file *file;
+  tree ident_filename = get_identifier (filename);
+
+  FOR_EACH_VEC_ELT (m_source_files, i, file)
+    if (file->filename_as_tree () == ident_filename)
+      return file;
+
+  /* Not found.  */
+  file = new source_file (ident_filename);
+  m_source_files.safe_push (file);
+  return file;
+}
+
+gcc::jit::source_file::source_file (tree filename) :
+  m_source_lines (),
+  m_filename (filename)
+{
+}
+
+gcc::jit::source_line *
+gcc::jit::source_file::
+get_source_line (int line_num)
+{
+  /* Locate the line.
+     For simplicitly, this is currently a linear search.
+     Replace with a hash if this shows up in the profile.  */
+  int i;
+  source_line *line;
+
+  FOR_EACH_VEC_ELT (m_source_lines, i, line)
+    if (line->get_line_num () == line_num)
+      return line;
+
+  /* Not found.  */
+  line = new source_line (this, line_num);
+  m_source_lines.safe_push (line);
+  return line;
+}
+
+gcc::jit::source_line::source_line (source_file *file, int line_num) :
+  m_locations (),
+  m_source_file (file),
+  m_line_num (line_num)
+{
+}
+
+gcc::jit::location *
+gcc::jit::source_line::
+get_location (int column_num)
+{
+  int i;
+  location *loc;
+
+  /* Another linear search that probably should be a hash table.  */
+  FOR_EACH_VEC_ELT (m_locations, i, loc)
+    if (loc->get_column_num () == column_num)
+      return loc;
+
+  /* Not found.  */
+  loc = new location (this, column_num);
+  m_locations.safe_push (loc);
+  return loc;
+}
+
+gcc::jit::location::location (source_line *line, int column_num) :
+  m_line (line),
+  m_column_num(column_num)
+{
+}
diff --git a/gcc/jit/internal-api.h b/gcc/jit/internal-api.h
index 7124d1d..32ec1ba 100644
--- a/gcc/jit/internal-api.h
+++ b/gcc/jit/internal-api.h
@@ -3,11 +3,15 @@
 #include "tree.h"
 #include "tree-iterator.h"
 
+#include <utility> // for std::pair
+
 namespace gcc {
 
 namespace jit {
 
 class result;
+class source_file;
+class source_line;
 class location;
 class type;
 class field;
@@ -31,6 +35,11 @@ public:
   set_code_factory (gcc_jit_code_callback cb,
 		    void *user_data);
 
+  location *
+  new_location (const char *filename,
+		int line,
+		int column);
+
   type *
   get_void_type ();
 
@@ -145,6 +154,15 @@ public:
   void
   add_error (const char *msg);
 
+  void
+  set_tree_location (tree t, location *loc);
+
+private:
+  source_file *
+  get_source_file (const char *filename);
+
+  void handle_locations ();
+
 private:
   gcc_jit_code_callback m_code_factory;
   bool m_within_code_factory;
@@ -168,6 +186,11 @@ private:
   bool m_bool_options[GCC_JIT_NUM_BOOL_OPTIONS];
   tree m_char_array_type_node;
   tree m_const_char_ptr;
+
+  /* Source location handling.  */
+  vec<source_file *> m_source_files;
+
+  vec<std::pair<tree, location *> > m_cached_locations;
 };
 
 /* The result of JIT-compilation.  */
@@ -202,10 +225,6 @@ public:
 
 };
 
-class location : public wrapper
-{
-};
-
 class type : public wrapper
 {
 public:
@@ -273,7 +292,7 @@ public:
 	     const char *name);
 
   void
-  place_forward_label (label *lab);
+  place_forward_label (location *loc, label *lab);
 
   void
   add_jump (location *loc,
@@ -294,6 +313,13 @@ public:
   context *m_ctxt;
 
 private:
+  void
+  set_tree_location (tree t, location *loc)
+  {
+    m_ctxt->set_tree_location (t, loc);
+  }
+
+private:
   tree m_inner_fndecl;
   enum gcc_jit_function_kind m_kind;
   tree m_stmt_list;
@@ -375,6 +401,70 @@ private:
   label *m_label_end;
 };
 
+/* Dealing with the linemap API.
+
+   It appears that libcpp requires locations to be created as if by
+   a tokenizer, creating them by filename, in ascending order of
+   line/column, whereas our API doesn't impose any such constraints:
+   we allow client code to create locations in arbitrary orders.
+
+   To square this circle, we need to cache all location creation,
+   grouping things up by filename/line, and then creating the linemap
+   entries in a post-processing phase.  */
+
+/* A set of locations, all sharing a filename */
+class source_file : public wrapper
+{
+public:
+  source_file (tree filename);
+
+  source_line *
+  get_source_line (int line_num);
+
+  tree filename_as_tree () const { return m_filename; }
+
+  const char*
+  get_filename () const { return IDENTIFIER_POINTER (m_filename); }
+
+  vec<source_line *> m_source_lines;
+
+private:
+  tree m_filename;
+};
+
+/* A source line, with one or more locations of interest.  */
+class source_line : public wrapper
+{
+public:
+  source_line (source_file *file, int line_num);
+
+  location *
+  get_location (int column_num);
+
+  int get_line_num () const { return m_line_num; }
+
+  vec<location *> m_locations;
+
+private:
+  source_file *m_source_file;
+  int m_line_num;
+};
+
+/* A specific location on a source line.  This is what we expose
+   to the client API.  */
+class location : public wrapper
+{
+public:
+  location (source_line *line, int column_num);
+
+  int get_column_num () const { return m_column_num; }
+
+  source_location m_srcloc;
+
+private:
+  source_line *m_line;
+  int m_column_num;
+};
 
 } // namespace gcc::jit
 
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 3dceba9..50ccae1 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -90,6 +90,16 @@ gcc_jit_context_set_code_factory (gcc_jit_context *ctxt,
     gcc_assert (!(CTXT)->within_code_factory ()); \
   } while (0)
 
+gcc_jit_location *
+gcc_jit_context_new_location (gcc_jit_context *ctxt,
+			      const char *filename,
+			      int line,
+			      int column)
+{
+  ASSERT_WITHIN_CALLBACK (ctxt);
+  return (gcc_jit_location *)ctxt->new_location (filename, line, column);
+}
+
 gcc_jit_type *
 gcc_jit_context_get_void_type (gcc_jit_context *ctxt)
 {
@@ -343,9 +353,10 @@ gcc_jit_function_add_label (gcc_jit_function *func,
 
 void
 gcc_jit_function_place_forward_label (gcc_jit_function *func,
+				      gcc_jit_location *loc,
 				      gcc_jit_label *lab)
 {
-  func->place_forward_label (lab);
+  func->place_forward_label (loc, lab);
 }
 
 void
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 88a8dd2..a17ab09 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -236,6 +236,13 @@ gcc_jit_result_release (gcc_jit_result *result);
 /**********************************************************************
  Functions for use within the code factory.
  **********************************************************************/
+/* Creating source code locations for use by the debugger.
+   Line and column numbers are 1-based.  */
+extern gcc_jit_location *
+gcc_jit_context_new_location (gcc_jit_context *ctxt,
+			      const char *filename,
+			      int line,
+			      int column);
 
 /* Access to specific types.  */
 extern gcc_jit_type *
@@ -442,6 +449,7 @@ gcc_jit_function_add_label (gcc_jit_function *func,
 
 extern void
 gcc_jit_function_place_forward_label (gcc_jit_function *func,
+				      gcc_jit_location *loc,
 				      gcc_jit_label *lab);
 
 extern void
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 9593672..859dc5e 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -4,6 +4,7 @@
     gcc_jit_context_acquire;
     gcc_jit_context_release;
     gcc_jit_context_set_code_factory;
+    gcc_jit_context_new_location;
     gcc_jit_context_get_void_type;
     gcc_jit_context_get_char_type;
     gcc_jit_context_get_int_type;
@@ -49,11 +50,5 @@
     gcc_jit_result_get_code;
     gcc_jit_result_release;
 
-    # For now:
-    main;
-
-    # For use by test functions:
-    called_function;
-
   local: *;
 };
\ No newline at end of file
diff --git a/gcc/testsuite/ChangeLog.jit b/gcc/testsuite/ChangeLog.jit
index 40fb34d..3d63d4b 100644
--- a/gcc/testsuite/ChangeLog.jit
+++ b/gcc/testsuite/ChangeLog.jit
@@ -1,3 +1,13 @@
+2013-10-14  David Malcolm  <dmalcolm@redhat.com>
+
+	* jit.dg/test-factorial.c (code_making_callback): Update
+	for change to gcc_jit_function_place_forward_label.
+	* jit.dg/test-fibonacci.c (code_making_callback): Add line
+	numbering to comment, and set up source locations throughout)
+	allowing stepping throught the comment in the debugger.
+	* jit.dg/test-sum-of-squares.c (code_making_callback): Update
+	for change to gcc_jit_function_place_forward_label.
+
 2013-10-10  David Malcolm  <dmalcolm@redhat.com>
 
 	* jit.dg/harness.h: Set GCC_JIT_BOOL_OPTION_DUMP_SUMMARY when
diff --git a/gcc/testsuite/jit.dg/test-factorial.c b/gcc/testsuite/jit.dg/test-factorial.c
index d3b83ba..90b4fbe 100644
--- a/gcc/testsuite/jit.dg/test-factorial.c
+++ b/gcc/testsuite/jit.dg/test-factorial.c
@@ -57,7 +57,7 @@ code_making_callback (gcc_jit_context *ctxt, void *user_data)
     label_false);
 
   /* true branch: */
-  gcc_jit_function_place_forward_label (func, label_true);
+  gcc_jit_function_place_forward_label (func, NULL, label_true);
   /* return x */
   gcc_jit_function_add_return (
     func,
@@ -65,7 +65,7 @@ code_making_callback (gcc_jit_context *ctxt, void *user_data)
     gcc_jit_param_as_rvalue (x));
 
   /* false branch: */
-  gcc_jit_function_place_forward_label (func, label_false);
+  gcc_jit_function_place_forward_label (func, NULL, label_false);
   gcc_jit_rvalue *x_minus_1 =
     gcc_jit_context_new_binary_op (
       ctxt, NULL,
diff --git a/gcc/testsuite/jit.dg/test-fibonacci.c b/gcc/testsuite/jit.dg/test-fibonacci.c
index 235e86e..215546c 100644
--- a/gcc/testsuite/jit.dg/test-fibonacci.c
+++ b/gcc/testsuite/jit.dg/test-fibonacci.c
@@ -9,25 +9,41 @@
 int
 code_making_callback (gcc_jit_context *ctxt, void *user_data)
 {
+  const int FIRST_LINE = __LINE__ + 4;
   /* Let's try to inject the equivalent of:
+0000000001111111111222222222233333333334444444444555555555566666666667
+1234567890123456789012345678901234567890123456789012345678901234567890
+FIRST_LINE + 0: int
+FIRST_LINE + 1: my_fibonacci (int x)
+FIRST_LINE + 2: {
+FIRST_LINE + 3:   if (x < 2)
+FIRST_LINE + 4:     return x;
+FIRST_LINE + 5:   else
+FIRST_LINE + 6:     return my_fibonacci (x - 1) + my_fibonacci (x - 2);
+FIRST_LINE + 7: }
+0000000001111111111222222222233333333334444444444555555555566666666667
+1234567890123456789012345678901234567890123456789012345678901234567890
 
-      fib.c: 1:   int
-      fib.c: 2:   my_fibonacci (int x)
-      fib.c: 3:   {
-      fib.c: 4:     if (x < 2)
-      fib.c: 5:       return x;
-      fib.c: 6:     else
-      fib.c: 7        return my_fibonacci (x - 1) + my_fibonacci (x - 2);
-      fib.c: 8    }
+     where the source locations are set up to point to the commented-out
+     code above.
+     It should therefore be possible to step through the generated code
+     in the debugger, stepping through the above commented-out code
+     fragement.
    */
   gcc_jit_type *the_type = gcc_jit_context_get_int_type (ctxt);
   gcc_jit_type *return_type = the_type;
 
   gcc_jit_param *x =
-    gcc_jit_context_new_param (ctxt, NULL, the_type, "x");
+    gcc_jit_context_new_param (
+      ctxt,
+      gcc_jit_context_new_location (
+	ctxt, __FILE__, FIRST_LINE + 1, 35),
+      the_type, "x");
   gcc_jit_param *params[1] = {x};
   gcc_jit_function *func =
-    gcc_jit_context_new_function (ctxt, NULL,
+    gcc_jit_context_new_function (ctxt,
+				  gcc_jit_context_new_location (
+				    ctxt, __FILE__, FIRST_LINE, 17),
 				  GCC_JIT_FUNCTION_EXPORTED,
 				  return_type,
 				  "my_fibonacci",
@@ -42,9 +58,11 @@ code_making_callback (gcc_jit_context *ctxt, void *user_data)
 
  /* if (x < 2) */
   gcc_jit_function_add_conditional (
-    func, NULL,
+    func,
+    gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 3, 19),
     gcc_jit_context_new_comparison (
-      ctxt, NULL,
+      ctxt,
+      gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 3, 25),
       GCC_JIT_COMPARISON_LT,
       gcc_jit_param_as_rvalue (x),
       gcc_jit_context_new_rvalue_from_int (
@@ -55,18 +73,25 @@ code_making_callback (gcc_jit_context *ctxt, void *user_data)
     label_false);
 
   /* true branch: */
-  gcc_jit_function_place_forward_label (func, label_true);
+  gcc_jit_function_place_forward_label (
+    func,
+    gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 4, 21),
+    label_true);
   /* return x */
   gcc_jit_function_add_return (
     func,
-    NULL,
+    gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 4, 21),
     gcc_jit_param_as_rvalue (x));
 
   /* false branch: */
-  gcc_jit_function_place_forward_label (func, label_false);
+  gcc_jit_function_place_forward_label (
+    func,
+    gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 6, 21),
+    label_false);
   gcc_jit_rvalue *x_minus_1 =
     gcc_jit_context_new_binary_op (
-      ctxt, NULL,
+      ctxt,
+      gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 6, 44),
       GCC_JIT_BINARY_OP_MINUS, the_type,
       gcc_jit_param_as_rvalue (x),
       gcc_jit_context_new_rvalue_from_int (
@@ -75,7 +100,8 @@ code_making_callback (gcc_jit_context *ctxt, void *user_data)
 	1));
   gcc_jit_rvalue *x_minus_2 =
     gcc_jit_context_new_binary_op (
-      ctxt, NULL,
+      ctxt,
+      gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 6, 67),
       GCC_JIT_BINARY_OP_MINUS, the_type,
       gcc_jit_param_as_rvalue (x),
       gcc_jit_context_new_rvalue_from_int (
@@ -84,20 +110,23 @@ code_making_callback (gcc_jit_context *ctxt, void *user_data)
 	2));
   gcc_jit_function_add_return (
     func,
-    NULL,
+    gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 6, 21),
     gcc_jit_context_new_binary_op (
-      ctxt, NULL,
+      ctxt,
+      gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 6, 49),
       GCC_JIT_BINARY_OP_PLUS, the_type,
       /* my_fibonacci (x - 1) */
       gcc_jit_context_new_call (
-        ctxt, NULL,
-        func,
-        1, &x_minus_1),
+	ctxt,
+	gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 6, 28),
+	func,
+	1, &x_minus_1),
       /* my_fibonacci (x - 2) */
       gcc_jit_context_new_call (
-        ctxt, NULL,
-        func,
-        1, &x_minus_2)));
+	ctxt,
+	gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 6, 51),
+	func,
+	1, &x_minus_2)));
   return 0;
 }
 
diff --git a/gcc/testsuite/jit.dg/test-sum-of-squares.c b/gcc/testsuite/jit.dg/test-sum-of-squares.c
index 4f1685d..bef0457 100644
--- a/gcc/testsuite/jit.dg/test-sum-of-squares.c
+++ b/gcc/testsuite/jit.dg/test-sum-of-squares.c
@@ -106,7 +106,7 @@ code_making_callback (gcc_jit_context *ctxt, void *user_data)
     label_cond);
 
   /* label "after_loop:" */
-  gcc_jit_function_place_forward_label (func, label_after_loop);
+  gcc_jit_function_place_forward_label (func, NULL, label_after_loop);
 
   /* return sum */
   gcc_jit_function_add_return (
-- 
1.7.11.7



More information about the Gcc-patches mailing list