github.com/cloudwego/frugal@v0.1.15/internal/atm/ssa/cfg_test.go (about)

     1  /*
     2   * Copyright 2022 ByteDance Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package ssa
    18  
    19  import (
    20      `fmt`
    21      `html`
    22      `io/ioutil`
    23      `strings`
    24      `testing`
    25  
    26      `github.com/cloudwego/frugal/internal/atm/hir`
    27  )
    28  
    29  func dumpbb(bb *BasicBlock) string {
    30      var w int
    31      var phi []string
    32      var ins []string
    33      var term []string
    34      for _, v := range bb.Phi {
    35          for _, ss := range strings.Split(v.String(), "\n") {
    36              vv := html.EscapeString(ss)
    37              vv = strings.ReplaceAll(vv, "$", "$$")
    38              phi = append(phi, fmt.Sprintf("<tr><td align=\"left\">%s</td></tr>\n", vv))
    39              if len(ss) > w {
    40                  w = len(ss)
    41              }
    42          }
    43      }
    44      for _, v := range bb.Ins {
    45          for _, ss := range strings.Split(v.String(), "\n") {
    46              vv := html.EscapeString(ss)
    47              vv = strings.ReplaceAll(vv, "$", "$$")
    48              ins = append(ins, fmt.Sprintf("<tr><td align=\"left\">%s</td></tr>\n", vv))
    49              if len(ss) > w {
    50                  w = len(ss)
    51              }
    52          }
    53      }
    54      for _, ss := range strings.Split(bb.Term.String(), "\n") {
    55          vv := html.EscapeString(ss)
    56          vv = strings.ReplaceAll(vv, "$", "$$")
    57          term = append(term, fmt.Sprintf("<tr><td align=\"left\">%s</td></tr>\n", vv))
    58          if len(ss) > w {
    59              w = len(ss)
    60          }
    61      }
    62      buf := []string {
    63          "<table border=\"1\" cellborder=\"0\" cellspacing=\"0\">\n",
    64          fmt.Sprintf("<tr><td width=\"%d\">bb_%d</td></tr>\n", w * 10 + 5, bb.Id),
    65      }
    66      if len(bb.Phi) != 0 {
    67          buf = append(buf, "<hr/>\n")
    68          buf = append(buf, phi...)
    69      }
    70      if len(bb.Ins) != 0 {
    71          buf = append(buf, "<hr/>\n")
    72          buf = append(buf, ins...)
    73      }
    74      buf = append(buf, "<hr/>\n")
    75      buf = append(buf, term...)
    76      buf = append(buf, "</table>")
    77      return strings.Join(buf, "")
    78  }
    79  
    80  func cfgdot(cfg *CFG, fn string) {
    81      e := make(map[[2]int]bool)
    82      buf := []string {
    83          "digraph CFG {",
    84          `    xdotversion = "15"`,
    85          `    graph [ fontname = "Fira Code" ]`,
    86          `    node [ fontname = "Fira Code" fontsize = "16" shape = "plaintext" ]`,
    87          `    edge [ fontname = "Fira Code" ]`,
    88          `    START [ shape = "circle" ]`,
    89          fmt.Sprintf(`    START -> bb_%d`, cfg.Root.Id),
    90      }
    91      for _, p := range cfg.PostOrder().Reversed() {
    92          f := true
    93          it := p.Term.Successors()
    94          buf = append(buf, fmt.Sprintf(`    bb_%d [ label = < %s > ]`, p.Id, dumpbb(p)))
    95          for it.Next() {
    96              ln := it.Block()
    97              edge := [2]int{p.Id, ln.Id}
    98              if !e[edge] {
    99                  e[edge] = true
   100                  if v, ok := it.Value(); ok {
   101                      f = false
   102                      buf = append(buf, fmt.Sprintf(`    bb_%d -> bb_%d [ label = "%d" ]`, p.Id, ln.Id, v))
   103                  } else if f {
   104                      buf = append(buf, fmt.Sprintf(`    bb_%d -> bb_%d [ label = "goto" ]`, p.Id, ln.Id))
   105                  } else {
   106                      buf = append(buf, fmt.Sprintf(`    bb_%d -> bb_%d [ label = "otherwise" ]`, p.Id, ln.Id))
   107                  }
   108              }
   109          }
   110      }
   111      buf = append(buf, "}")
   112      err := ioutil.WriteFile(fn, []byte(strings.Join(buf, "\n")), 0644)
   113      if err != nil {
   114          panic(err)
   115      }
   116  }
   117  
   118  var (
   119      ftest = hir.RegisterGCall(func (i int) int { return i + 1 }, nil)
   120  )
   121  
   122  func TestCFG_Build(t *testing.T) {
   123      p := hir.CreateBuilder()
   124      p.LDAP  (0, hir.P0)
   125      p.LDAP  (1, hir.P1)
   126      p.Label ("loop")
   127      p.LQ    (hir.P0, 8, hir.R0)
   128      p.SUBI  (hir.R0, 1, hir.R0)
   129      p.GCALL (ftest).A0(hir.R0).R0(hir.R2)
   130      p.SQ    (hir.R2, hir.P0, 8)
   131      p.BNE   (hir.R0, hir.Rz, "loop")
   132      p.LQ    (hir.P1, 8, hir.R1)
   133      p.RET   ().R0(hir.R0).R1(hir.R1)
   134      t.Logf("Generating CFG ...")
   135      c := p.Build()
   136      g := Compile(c, (func(*int, *int) (int, int))(nil))
   137      t.Logf("Generating DOT file ...")
   138      cfgdot(g, "/tmp/cfg.gv")
   139  }