shithub: mc

ref: e1e9167cc8b847dfb3b37df4dc041d20ac271010
dir: /mbld/syssel.myr/

View raw version
use std
use bio

use "opts"
use "types"

pkg bld =
	type syssel(@a) = struct
		file	: byte[:]
		line	: int
		targ	: byte[:]
		_match	: std.htab(byte[:], (int, int))#
		_best	: std.htab(byte[:], @a)#
	;;

	generic mksyssel	: (b : build#, f : byte[:], line : int, targ : byte[:] -> syssel(@a)#)
	generic sysseladd	: (b : build#, syssel : syssel(byte[:])#, file : byte[:] -> void)
	generic sysseladdlist	: (b : build#, syssel : syssel(@a)#, base : byte[:], attrs : byte[:][:], val : @a -> void)
	generic sysselfin	: (b : build#, syssel : syssel(@a)# -> @a[:])
	const addsysattrs	: (sa : build#, tags : byte[:][:] -> void)
;;

generic mksyssel = {b, file, line, targ
	var syssel

	syssel = std.mk([
		.file = file,
		.line = line,
		.targ = targ,
		._match = std.mkht(),
		._best = std.mkht(),
	])
	-> syssel
}

generic sysseladd = {b, syssel, f
	var basename, attrs
	var attrlist

	match std.strfind(f, "+")
	| `std.Some i:
		basename = f[:i]
		match std.strrfind(f[i+1:], ".")
		| `std.Some j:	attrs = f[i+1:][:j]
		| `std.None:	std.fatal("unrecognized type for file {}\n", f)
		;;
	| `std.None:
		match std.strrfind(f, ".")
		| `std.None:	std.fatal("unrecognized type for file {}\n", f)
		| `std.Some i:
			basename = f[:i]
			attrs = ""
		;;
	;;

	attrlist = std.strsplit(attrs, "-")
	sysseladdlist(b, syssel, basename, attrlist, f)
	std.slfree(attrlist)
}

generic sysseladdlist = {b, syssel, base, attrs, val
	var nmatch, vscore, s

	nmatch = 0
	vscore = 0
	for a : attrs
		s = tagscore(b, a)
		if s < 0
			nmatch = -1
			break
		;;
		vscore += s
		nmatch++
	;;
	match std.htgetv(syssel._match, base, (-1, -1))
	| (curbest, curscore):
		if curbest < nmatch || (nmatch >= 0 && curbest == nmatch && curscore < vscore)
			std.htput(syssel._match, base, (nmatch, vscore))
			std.htput(syssel._best, base, val)
		;;
	;;
}

const tagscore = {b, tag
	var n, v

	match std.strfind(tag, ":")
	| `std.Some i:
		n = tag[:i]
		v = parseversion(tag[i+1:])
	| `std.None:
		n = tag
		v = (-1, -1, -1)
	;;

	match std.htget(b.tags, n)
	| `std.None:	-> -1
	| `std.Some cv:	-> versionscore(cv, v)
	;;
}

const versionscore = {have, want
	var s

	s = 0
	match (have, want)
	| ((a0, a1, a2), (v0, v1, v2)):
		if a0 == -1 && a1 == -1 && a2 == -1
			-> s
		elif v0 == -1 && v1 == -1 && v2 == -1
			-> s
		else
			s = 1_000_000 * (a0 - v0)
			if s == 0
				s += 1_000 * (a1 - v1)
			;;
			if s == 0
				s += (a2 - v2)
			;;
			if s >= 0
				s = 100_000_000 - s
			;;
			-> s
		;;
	;;
}

generic sysselfin = {b, syssel
	var keys, nmatch, ret

	keys = std.htkeys(syssel._match)
	ret = [][:]
	for k : keys
		(nmatch, _) = std.htgetv(syssel._match, k, (-1, -1))
		if nmatch == -1
			std.fatal("{}:{}: target {}, no applicable file for '{}'\n", \
				syssel.file, syssel.line, syssel.targ, k)
		;;
		std.slpush(&ret, std.get(std.htget(syssel._best, k)))
	;;
	std.htfree(syssel._match)
	std.htfree(syssel._best)
	-> ret
}

const addsysattrs = {b, tags
	if opt_alltags.len > 0
		for t : opt_alltags
			tag(b, t)
		;;
	else
		std.htput(b.tags, opt_sys, opt_sysvers)

		match opt_sys
		| "freebsd":	tag(b, "posixy")
		| "netbsd":	tag(b, "posixy")
		| "openbsd":	tag(b, "posixy")
		| "osx":	tag(b, "posixy")
		| "linux":	tag(b, "posixy")
		| "plan9":
		| unknown:	std.fatal("unknown system \"{}\"\n", unknown)
		;;

		match opt_arch
		| "x64":	
			tag(b, "x64")
			if supports(CpuidSSE2)
				tag(b, "sse2")
			;;
			if supports(CpuidSSE4)
				tag(b, "sse4")
			;;
			if supports(CpuidFMA)
				tag(b, "fma")
			;;
		| unknown:
			std.fatal("unknown architecture {}\n", unknown)
		;;

		for t : tags
			tag(b, t)
		;;
		loadtagfile(b, "bld.tags")
	;;
}

const supports = {feat
	match (opt_sys, opt_cpufeatures & feat)
	/* The version of gas that's shipped with openbsd is too old. */
	| ("openbsd", CpuidSSE4):	-> false
	| ("openbsd", CpuidFMA):	-> false
	| ("freebsd", CpuidSSE4):	-> false
	| ("freebsd", CpuidFMA):	-> false
	| ("netbsd", CpuidSSE4):	-> false
	| ("netbsd", CpuidFMA):	-> false
	| (_, f):			-> f == feat
	;;
}

const loadtagfile = {b, tagfile
	var deptags, tagmap, changed
	var tf, lnum

	if !std.fexists(tagfile)
		-> void
	;;

	match bio.open(tagfile, bio.Rd)
	| `std.Ok f:	tf = f
	| `std.Err e:	std.fatal("could not open tagfile: {}\n", e)
	;;

	lnum = 0
	tagmap = std.mkht()
	/*
	 * Parse the list of tags. Each line is in the form of
	 *
	 *	tag list ":" tag list
	 * 
	 * The left hand side of the tag list describes the tags
	 * that get added if the tags on the right hand side all
	 * are present.
	 */
	for ln : bio.byline(tf)
		lnum++
		match std.strfind(ln, ":")
		| `std.None:
			for t : std.bytok(ln)
				tag(b, t)
			;;
			continue
		| `std.Some idx:
			if std.strstrip(ln[:idx]).len == 0
				std.fatal("{}:{}: missing tags before ':'\n", tagfile, lnum)
			;;
			deptags = [][:]
			for d : std.bytok(ln[idx+1:])
				std.slpush(&deptags, std.sldup(d))
			;;
			for t : std.bytok(ln[:idx])
				match std.htget(tagmap, t)
				| `std.Some v:
					std.slpush(&v, deptags)
					std.htput(tagmap, t, v)
				| `std.None:
					std.htput(tagmap, std.sldup(t), std.sldup([deptags][:]))
				;;
			;;
		;;
	;;
	bio.close(tf)
	
	/* 
	 * Because tags may depend on other tags, we need to iterate
	 * here until the set of tags reach a fixed point. Each tag
	 * that we insert may potentially free other tags to be inserted,
	 * so either we make progress on the finite set of tags, or we
	 * don't make a change and break out of the loop.
	 */
	changed = true
	while changed
		changed = false
		for (k, vss) : std.byhtkeyvals(tagmap)
			if std.hthas(b.tags, k)
				continue
			;;
			for vs : vss
				for v : vs
					if tagscore(b, v) == -1
						goto next
					;;
				;;
				tag(b, k)
				changed = true
:next
			;;
		;;
	;;
	for (k, vss) : std.byhtkeyvals(tagmap)
		std.slfree(k)
		for vs : vss
			for v : vs
				std.slfree(v)
			;;
			std.slfree(vs)
		;;
		std.slfree(vss)
	;;
}

const tag  = {b, t
	var v
	match std.strfind(t, ":")
	| `std.None:
		std.htput(b.tags, std.sldup(t), (-1, -1, -1))
	| `std.Some idx:
		v = parseversion(t[idx+1:])
		std.htput(b.tags, std.sldup(t[:idx]), v)
	;;

}