~andyc/oil

1f9330d5d6f837e930ffae30ff0f9f880eec017d — Andy Chu 2 months ago e8a0402
[mycpp] Add StackRoots for (some) loop index variables.

It would be nice to have something more principled.

- fix Str::index() to accept negative indices
- fix Str::join() to use 'self'
- devtools: Add pretty printer for gc_heap::Str
5 files changed, 49 insertions(+), 7 deletions(-)

M devtools/oil_gdb.py
M mycpp/cppgen_pass.py
M mycpp/my_runtime.cc
M mycpp/mylib2.cc
M mycpp/setup.sh
M devtools/oil_gdb.py => devtools/oil_gdb.py +23 -0
@@ 45,6 45,25 @@ class StrPrinter:
        return data_.lazy_string('ascii', len_)


class GcStrPrinter:
    """Print gc_heap::Str type"""

    def __init__(self, val):
        self.val = val

    def to_string(self):
        # Unused because GDB in CLion doesn't want us to print past end of array?
        len_ = self.val['obj_len_'] - 12 - 1

        # This is a gdb.Value object
        data_ = self.val['data_']
        
        # This only prints until first NUL, since we don't have the length.
        # note: lazy_string() only prints the first char!
        t = gdb.lookup_type('char*')
        return data_.reinterpret_cast(t)


class AsdlPrinter(object):
    """Print the variants of a particular ASDL sum type.



@@ 133,6 152,10 @@ class TypeLookup(object):
      #print('target name %r' % target.name)
      #print('target tag %r' % target.tag)

      if target.name == 'gc_heap::Str':
          return GcStrPrinter(val)

      # TODO: remove Str when we remove mylib.cc
      if target.name == 'Str':
          return StrPrinter(val)


M mycpp/cppgen_pass.py => mycpp/cppgen_pass.py +9 -1
@@ 1352,6 1352,7 @@ class Generate(ExpressionVisitor[T], StatementVisitor[None]):

            if isinstance(item_type, Instance):
              self.write_ind('  %s ', get_c_type(item_type))
              # TODO(StackRoots): for ch in 'abc'
              self.accept(index_expr)
              self.write(' = it.Value();\n')
            


@@ 1614,6 1615,11 @@ class Generate(ExpressionVisitor[T], StatementVisitor[None]):
          else: 
            self.write(' = it.Value();\n')

          # Register loop variable as a stack root.
          self.write_ind('  StackRoots _for({&');
          self.accept(index_expr)
          self.write_ind('});\n')

        elif isinstance(item_type, TupleType):  # for x, y in pairs
          if over_dict:
            assert isinstance(o.index, TupleExpr), o.index


@@ 1624,6 1630,7 @@ class Generate(ExpressionVisitor[T], StatementVisitor[None]):
            key_type = get_c_type(item_type.items[0])
            val_type = get_c_type(item_type.items[1])

            # TODO(StackRoots): k, v
            self.write_ind('  %s %s = it.Key();\n', key_type, index_items[0].name)
            self.write_ind('  %s %s = it.Value();\n', val_type, index_items[1].name)



@@ 1639,6 1646,7 @@ class Generate(ExpressionVisitor[T], StatementVisitor[None]):
            c_item_type = get_c_type(item_type)

            if isinstance(o.index, TupleExpr):
              # TODO(StackRoots)
              temp_name = 'tup%d' % self.unique_id
              self.unique_id += 1
              self.write_ind('  %s %s = it.Value();\n', c_item_type, temp_name)


@@ 1651,6 1659,7 @@ class Generate(ExpressionVisitor[T], StatementVisitor[None]):
              self.indent -= 1
            else:
              self.write_ind('  %s %s = it.Value();\n', c_item_type, o.index.name)
              #self.write_ind('  StackRoots _for(&%s)\n;', o.index.name)

        else:
          raise AssertionError('Unexpected type %s' % item_type)


@@ 2372,7 2381,6 @@ class Generate(ExpressionVisitor[T], StatementVisitor[None]):
              roots.append(lval_name)
          self.log('roots %s', roots)

          # TODO: Also need function parameters
          if len(roots):
            self.write_ind('StackRoots _roots({');
            for i, r in enumerate(roots):

M mycpp/my_runtime.cc => mycpp/my_runtime.cc +12 -4
@@ 335,7 335,7 @@ Str* Str::replace(Str* old, Str* new_str) {
  assert(len(old) == 1);  // Restriction that Oil code is OK with

  Str* self = this;       // must be a root!
  Str* result = nullptr;  // ditto
  Str* result = nullptr;  // may not need to be a root, but make it robust 

  // log("  self BEFORE %p", self);
  StackRoots _roots({&self, &old, &new_str, &result});


@@ 430,6 430,11 @@ List<Str*>* Str::split(Str* sep) {
}

Str* Str::join(List<Str*>* items) {
  Str* self = this;       // must be a root!
  Str* result = nullptr;  // may not need to be a root, but make it robust 

  StackRoots _roots({&self, &result, &items});

  int result_len = 0;
  int num_parts = len(items);
  if (num_parts == 0) {  // " ".join([]) == ""


@@ 438,14 443,14 @@ Str* Str::join(List<Str*>* items) {
  for (int i = 0; i < num_parts; ++i) {
    result_len += len(items->index(i));
  }
  int sep_len = len(this);
  int sep_len = len(self);
  // add length of all the separators
  result_len += sep_len * (num_parts - 1);

  // log("len: %d", len);
  // log("v.size(): %d", v.size());

  Str* result = NewStr(result_len);
  result = NewStr(result_len);
  char* p_result = result->data_;  // advances through

  for (int i = 0; i < num_parts; ++i) {


@@ 457,7 462,10 @@ Str* Str::join(List<Str*>* items) {
    }

    int n = len(items->index(i));
    // log("n: %d", n);
    if (n < 0) {
      log("n: %d", n);
      assert(0);
    }
    memcpy(p_result, items->index(i)->data_, n);  // copy the list item
    p_result += n;
  }

M mycpp/mylib2.cc => mycpp/mylib2.cc +3 -0
@@ 91,6 91,9 @@ void BufWriter::write(Str* s) {

  // BUG: This is quadratic!

  // TODO: change this to append to a list?
  // and then getvalue() does a join() on it?

  // data_ is nullptr at first
  data_ = static_cast<char*>(realloc(data_, len_ + 1));


M mycpp/setup.sh => mycpp/setup.sh +2 -2
@@ 64,7 64,7 @@ build-examples() {
test-examples() {
  ### Test all mycpp/examples

  # crashes on 'loops'
  # This works!
  # export GC=1

  cd $THIS_DIR


@@ 74,7 74,7 @@ test-examples() {
benchmark-examples() {
  ### Benchmark all mycpp/examples

  # crashes on 'escape', which uses join()
  # crashes on 'files', which uses BufWriter
  # export GC=1

  cd $THIS_DIR