Example #1
0
File: ssa.go Project: pcc/llgo
// translatePackage translates an *ssa.Package into an LLVM module, and returns
// the translation unit information.
func (u *unit) translatePackage(pkg *ssa.Package) {
	// Initialize global storage.
	for _, m := range pkg.Members {
		switch v := m.(type) {
		case *ssa.Global:
			llelemtyp := u.llvmtypes.ToLLVM(deref(v.Type()))
			global := llvm.AddGlobal(u.module.Module, llelemtyp, v.String())
			global.SetInitializer(llvm.ConstNull(llelemtyp))
			u.globals[v] = u.NewValue(global, v.Type())
		}
	}

	// Define functions.
	// Sort if flag is set for deterministic behaviour (for debugging)
	functions := ssautil.AllFunctions(pkg.Prog)
	if !u.compiler.OrderedCompilation {
		for f, _ := range functions {
			u.defineFunction(f)
		}
	} else {
		fns := []*ssa.Function{}
		for f, _ := range functions {
			fns = append(fns, f)
		}
		sort.Sort(byName(fns))
		for _, f := range fns {
			u.defineFunction(f)
		}
	}

	// Define remaining functions that were resolved during
	// runtime type mapping, but not defined.
	for f, _ := range u.undefinedFuncs {
		u.defineFunction(f)
	}
}
Example #2
0
// peers enumerates, for a given channel send (or receive) operation,
// the set of possible receives (or sends) that correspond to it.
//
// TODO(adonovan): support reflect.{Select,Recv,Send}.
// TODO(adonovan): permit the user to query based on a MakeChan (not send/recv),
// or the implicit receive in "for v := range ch".
//
func peers(o *Oracle, qpos *QueryPos) (queryResult, error) {
	arrowPos := findArrow(qpos)
	if arrowPos == token.NoPos {
		return nil, fmt.Errorf("there is no send/receive here")
	}

	buildSSA(o)

	var queryOp chanOp // the originating send or receive operation
	var ops []chanOp   // all sends/receives of opposite direction

	// Look at all send/receive instructions in the whole ssa.Program.
	// Build a list of those of same type to query.
	allFuncs := ssautil.AllFunctions(o.prog)
	for fn := range allFuncs {
		for _, b := range fn.Blocks {
			for _, instr := range b.Instrs {
				for _, op := range chanOps(instr) {
					ops = append(ops, op)
					if op.pos == arrowPos {
						queryOp = op // we found the query op
					}
				}
			}
		}
	}
	if queryOp.ch == nil {
		return nil, fmt.Errorf("ssa.Instruction for send/receive not found")
	}

	// Discard operations of wrong channel element type.
	// Build set of channel ssa.Values as query to pointer analysis.
	// We compare channels by element types, not channel types, to
	// ignore both directionality and type names.
	queryType := queryOp.ch.Type()
	queryElemType := queryType.Underlying().(*types.Chan).Elem()
	o.ptaConfig.AddQuery(queryOp.ch)
	i := 0
	for _, op := range ops {
		if types.IsIdentical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) {
			o.ptaConfig.AddQuery(op.ch)
			ops[i] = op
			i++
		}
	}
	ops = ops[:i]

	// Run the pointer analysis.
	ptares := ptrAnalysis(o)

	// Combine the PT sets from all contexts.
	queryChanPts := pointer.PointsToCombined(ptares.Queries[queryOp.ch])

	// Ascertain which make(chan) labels the query's channel can alias.
	var makes []token.Pos
	for _, label := range queryChanPts.Labels() {
		makes = append(makes, label.Pos())
	}
	sort.Sort(byPos(makes))

	// Ascertain which send/receive operations can alias the same make(chan) labels.
	var sends, receives []token.Pos
	for _, op := range ops {
		for _, ptr := range ptares.Queries[op.ch] {
			if ptr.PointsTo().Intersects(queryChanPts) {
				if op.dir == types.SendOnly {
					sends = append(sends, op.pos)
				} else {
					receives = append(receives, op.pos)
				}
			}
		}
	}
	sort.Sort(byPos(sends))
	sort.Sort(byPos(receives))

	return &peersResult{
		queryPos:  arrowPos,
		queryType: queryType,
		makes:     makes,
		sends:     sends,
		receives:  receives,
	}, nil
}
func TestStdlib(t *testing.T) {
	impctx := importer.Config{Build: &build.Default}

	// Load, parse and type-check the program.
	t0 := time.Now()

	imp := importer.New(&impctx)

	if _, _, err := imp.LoadInitialPackages(allPackages()); err != nil {
		t.Errorf("LoadInitialPackages failed: %s", err)
		return
	}

	t1 := time.Now()

	runtime.GC()
	var memstats runtime.MemStats
	runtime.ReadMemStats(&memstats)
	alloc := memstats.Alloc

	// Create SSA packages.
	prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
	if err := prog.CreatePackages(imp); err != nil {
		t.Errorf("CreatePackages failed: %s", err)
		return
	}
	// Enable debug mode globally.
	for _, info := range imp.AllPackages() {
		prog.Package(info.Pkg).SetDebugMode(debugMode)
	}

	t2 := time.Now()

	// Build SSA IR... if it's safe.
	prog.BuildAll()

	t3 := time.Now()

	runtime.GC()
	runtime.ReadMemStats(&memstats)

	numPkgs := len(prog.AllPackages())
	if want := 140; numPkgs < want {
		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
	}

	// Dump some statistics.
	allFuncs := ssautil.AllFunctions(prog)
	var numInstrs int
	for fn := range allFuncs {
		for _, b := range fn.Blocks {
			numInstrs += len(b.Instrs)
		}
	}

	// determine line count
	var lineCount int
	imp.Fset.Iterate(func(f *token.File) bool {
		lineCount += f.LineCount()
		return true
	})

	// NB: when benchmarking, don't forget to clear the debug +
	// sanity builder flags for better performance.

	t.Log("GOMAXPROCS:           ", runtime.GOMAXPROCS(0))
	t.Log("#Source lines:        ", lineCount)
	t.Log("Load/parse/typecheck: ", t1.Sub(t0))
	t.Log("SSA create:           ", t2.Sub(t1))
	t.Log("SSA build:            ", t3.Sub(t2))

	// SSA stats:
	t.Log("#Packages:            ", numPkgs)
	t.Log("#Functions:           ", len(allFuncs))
	t.Log("#Instructions:        ", numInstrs)
	t.Log("#MB:                  ", (memstats.Alloc-alloc)/1000000)
}