Example #1
0
CGResult sst::TupleAssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer)
{
	cs->pushLoc(this);
	defer(cs->popLoc());

	auto tuple = this->right->codegen(cs).value;
	if(!tuple->getType()->isTupleType())
		error(this->right, "Expected tuple type in assignment to tuple on left-hand-side; found type '%s' instead", tuple->getType());

	auto tty = tuple->getType()->toTupleType();

	std::vector<CGResult> results;

	size_t idx = 0;
	for(auto v : this->lefts)
	{
		auto res = v->codegen(cs, tty->getElementN(idx));
		if(res->islvalue())
			error(v, "Cannot assign to non-lvalue expression in tuple assignment");

		results.push_back(res);
		idx++;
	}

	for(size_t i = 0; i < idx; i++)
	{
		auto lr = results[i];
		auto val = cs->irb.ExtractValue(tuple, { i });

		auto rr = cs->oneWayAutocast(val, lr.value->getType());
		if(!rr || rr->getType() != lr.value->getType())
		{
			error(this->right, "Mismatched types in assignment to tuple element %d; assigning type '%s' to '%s'",
				val->getType(), lr.value->getType());
		}

		cs->autoAssignRefCountedValue(lr.value, rr, /* isInitial: */ false, /* performStore: */ true);
	}

	return CGResult(0);
}
Example #2
0
File: racewalk.c Project: 8l/go
// walkexpr and walkstmt combined
// walks the tree and adds calls to the
// instrumentation code to top-level (statement) nodes' init
static void
racewalknode(Node **np, NodeList **init, int wr, int skip)
{
	Node *n, *n1;
	NodeList *l;
	NodeList *fini;

	n = *np;

	if(n == N)
		return;

	if(debug['w'] > 1)
		dump("racewalk-before", n);
	setlineno(n);
	if(init == nil)
		fatal("racewalk: bad init list");
	if(init == &n->ninit) {
		// If init == &n->ninit and n->ninit is non-nil,
		// racewalknode might append it to itself.
		// nil it out and handle it separately before putting it back.
		l = n->ninit;
		n->ninit = nil;
		racewalklist(l, nil);
		racewalknode(&n, &l, wr, skip);  // recurse with nil n->ninit
		appendinit(&n, l);
		*np = n;
		return;
	}

	racewalklist(n->ninit, nil);

	switch(n->op) {
	default:
		fatal("racewalk: unknown node type %O", n->op);

	case OASOP:
	case OAS:
	case OAS2:
	case OAS2RECV:
	case OAS2FUNC:
	case OAS2MAPR:
		racewalknode(&n->left, init, 1, 0);
		racewalknode(&n->right, init, 0, 0);
		goto ret;

	case OCFUNC:
	case OVARKILL:
		// can't matter
		goto ret;

	case OBLOCK:
		if(n->list == nil)
			goto ret;

		switch(n->list->n->op) {
		case OCALLFUNC:
		case OCALLMETH:
		case OCALLINTER:
			// Blocks are used for multiple return function calls.
			// x, y := f() becomes BLOCK{CALL f, AS x [SP+0], AS y [SP+n]}
			// We don't want to instrument between the statements because it will
			// smash the results.
			racewalknode(&n->list->n, &n->list->n->ninit, 0, 0);
			fini = nil;
			racewalklist(n->list->next, &fini);
			n->list = concat(n->list, fini);
			break;

		default:
			// Ordinary block, for loop initialization or inlined bodies.
			racewalklist(n->list, nil);
			break;
		}
		goto ret;

	case ODEFER:
		racewalknode(&n->left, init, 0, 0);
		goto ret;

	case OPROC:
		racewalknode(&n->left, init, 0, 0);
		goto ret;

	case OCALLINTER:
		racewalknode(&n->left, init, 0, 0);
		goto ret;

	case OCALLFUNC:
		// Instrument dst argument of runtime.writebarrier* calls
		// as we do not instrument runtime code.
		if(n->left->sym != S && n->left->sym->pkg == runtimepkg && strncmp(n->left->sym->name, "writebarrier", 12) == 0) {
			// Find the dst argument.
			// The list can be reordered, so it's not necessary just the first or the second element.
			for(l = n->list; l; l = l->next) {
				if(strcmp(n->left->sym->name, "writebarrierfat") == 0) {
					if(l->n->left->xoffset == widthptr)
						break;
				} else {
					if(l->n->left->xoffset == 0)
						break;
				}
			}
			if(l == nil)
				fatal("racewalk: writebarrier no arg");
			if(l->n->right->op != OADDR)
				fatal("racewalk: writebarrier bad arg");
			callinstr(&l->n->right->left, init, 1, 0);
		}
		racewalknode(&n->left, init, 0, 0);
		goto ret;

	case ONOT:
	case OMINUS:
	case OPLUS:
	case OREAL:
	case OIMAG:
	case OCOM:
		racewalknode(&n->left, init, wr, 0);
		goto ret;

	case ODOTINTER:
		racewalknode(&n->left, init, 0, 0);
		goto ret;

	case ODOT:
		racewalknode(&n->left, init, 0, 1);
		callinstr(&n, init, wr, skip);
		goto ret;

	case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND
		racewalknode(&n->left, init, 0, 0);
		callinstr(&n, init, wr, skip);
		goto ret;

	case OIND: // *p
		racewalknode(&n->left, init, 0, 0);
		callinstr(&n, init, wr, skip);
		goto ret;

	case OSPTR:
	case OLEN:
	case OCAP:
		racewalknode(&n->left, init, 0, 0);
		if(istype(n->left->type, TMAP)) {
			n1 = nod(OCONVNOP, n->left, N);
			n1->type = ptrto(types[TUINT8]);
			n1 = nod(OIND, n1, N);
			typecheck(&n1, Erv);
			callinstr(&n1, init, 0, skip);
		}
		goto ret;

	case OLSH:
	case ORSH:
	case OLROT:
	case OAND:
	case OANDNOT:
	case OOR:
	case OXOR:
	case OSUB:
	case OMUL:
	case OHMUL:
	case OEQ:
	case ONE:
	case OLT:
	case OLE:
	case OGE:
	case OGT:
	case OADD:
	case OCOMPLEX:
		racewalknode(&n->left, init, wr, 0);
		racewalknode(&n->right, init, wr, 0);
		goto ret;

	case OANDAND:
	case OOROR:
		racewalknode(&n->left, init, wr, 0);
		// walk has ensured the node has moved to a location where
		// side effects are safe.
		// n->right may not be executed,
		// so instrumentation goes to n->right->ninit, not init.
		racewalknode(&n->right, &n->right->ninit, wr, 0);
		goto ret;

	case ONAME:
		callinstr(&n, init, wr, skip);
		goto ret;

	case OCONV:
		racewalknode(&n->left, init, wr, 0);
		goto ret;

	case OCONVNOP:
		racewalknode(&n->left, init, wr, 0);
		goto ret;

	case ODIV:
	case OMOD:
		racewalknode(&n->left, init, wr, 0);
		racewalknode(&n->right, init, wr, 0);
		goto ret;

	case OINDEX:
		if(!isfixedarray(n->left->type))
			racewalknode(&n->left, init, 0, 0);
		else if(!islvalue(n->left)) {
			// index of unaddressable array, like Map[k][i].
			racewalknode(&n->left, init, wr, 0);
			racewalknode(&n->right, init, 0, 0);
			goto ret;
		}
		racewalknode(&n->right, init, 0, 0);
		if(n->left->type->etype != TSTRING)
			callinstr(&n, init, wr, skip);
		goto ret;

	case OSLICE:
	case OSLICEARR:
	case OSLICE3:
	case OSLICE3ARR:
		// Seems to only lead to double instrumentation.
		//racewalknode(&n->left, init, 0, 0);
		goto ret;

	case OADDR:
		racewalknode(&n->left, init, 0, 1);
		goto ret;

	case OEFACE:
		racewalknode(&n->left, init, 0, 0);
		racewalknode(&n->right, init, 0, 0);
		goto ret;

	case OITAB:
		racewalknode(&n->left, init, 0, 0);
		goto ret;

	// should not appear in AST by now
	case OSEND:
	case ORECV:
	case OCLOSE:
	case ONEW:
	case OXCASE:
	case OXFALL:
	case OCASE:
	case OPANIC:
	case ORECOVER:
	case OCONVIFACE:
	case OCMPIFACE:
	case OMAKECHAN:
	case OMAKEMAP:
	case OMAKESLICE:
	case OCALL:
	case OCOPY:
	case OAPPEND:
	case ORUNESTR:
	case OARRAYBYTESTR:
	case OARRAYRUNESTR:
	case OSTRARRAYBYTE:
	case OSTRARRAYRUNE:
	case OINDEXMAP:  // lowered to call
	case OCMPSTR:
	case OADDSTR:
	case ODOTTYPE:
	case ODOTTYPE2:
	case OAS2DOTTYPE:
	case OCALLPART: // lowered to PTRLIT
	case OCLOSURE:  // lowered to PTRLIT
	case ORANGE:    // lowered to ordinary for loop
	case OARRAYLIT: // lowered to assignments
	case OMAPLIT:
	case OSTRUCTLIT:
		yyerror("racewalk: %O must be lowered by now", n->op);
		goto ret;

	// impossible nodes: only appear in backend.
	case ORROTC:
	case OEXTEND:
		yyerror("racewalk: %O cannot exist now", n->op);
		goto ret;

	// just do generic traversal
	case OFOR:
	case OIF:
	case OCALLMETH:
	case ORETURN:
	case ORETJMP:
	case OSWITCH:
	case OSELECT:
	case OEMPTY:
	case OBREAK:
	case OCONTINUE:
	case OFALL:
	case OGOTO:
	case OLABEL:
		goto ret;

	// does not require instrumentation
	case OPRINT:     // don't bother instrumenting it
	case OPRINTN:    // don't bother instrumenting it
	case OCHECKNIL: // always followed by a read.
	case OPARAM:     // it appears only in fn->exit to copy heap params back
	case OCLOSUREVAR:// immutable pointer to captured variable
	case ODOTMETH:   // either part of CALLMETH or CALLPART (lowered to PTRLIT)
	case OINDREG:    // at this stage, only n(SP) nodes from nodarg
	case ODCL:       // declarations (without value) cannot be races
	case ODCLCONST:
	case ODCLTYPE:
	case OTYPE:
	case ONONAME:
	case OLITERAL:
	case OSLICESTR:  // always preceded by bounds checking, avoid double instrumentation.
	case OTYPESW:    // ignored by code generation, do not instrument.
		goto ret;
	}

ret:
	if(n->op != OBLOCK)  // OBLOCK is handled above in a special way.
		racewalklist(n->list, init);
	if(n->ntest != N)
		racewalknode(&n->ntest, &n->ntest->ninit, 0, 0);
	if(n->nincr != N)
		racewalknode(&n->nincr, &n->nincr->ninit, 0, 0);
	racewalklist(n->nbody, nil);
	racewalklist(n->nelse, nil);
	racewalklist(n->rlist, nil);
	*np = n;
}
Example #3
0
CGResult sst::AssignOp::_codegen(cgn::CodegenState* cs, fir::Type* infer)
{
	cs->pushLoc(this);
	defer(cs->popLoc());

	auto lr = this->left->codegen(cs).value;
	auto lt = lr->getType();

	if(!lr->islorclvalue())
	{
		SpanError::make(SimpleError::make(this->loc, "Cannot assign to non-lvalue (most likely a temporary) expression"))
			->add(util::ESpan(this->left->loc, "here"))
			->postAndQuit();
	}
	else if(lr->isclvalue())
	{
		SpanError::make(SimpleError::make(this->loc, "Cannot assign to immutable expression"))
			->add(util::ESpan(this->left->loc, "here"))
			->postAndQuit();
	}


	// okay, i guess
	auto rr = this->right->codegen(cs, lt).value;
	auto rt = rr->getType();

	if(this->op != Operator::Assign)
	{
		// ok it's a compound assignment
		// auto [ newl, newr ] = cs->autoCastValueTypes(lr, rr);
		auto nonass = Operator::getNonAssignmentVersion(this->op);

		// some things -- if we're doing +=, and the types are supported, then just call the actual
		// append function, instead of doing the + first then assigning it.

		if(nonass == Operator::Plus)
		{
			if(lt->isDynamicArrayType() && lt == rt)
			{
				// right then.
				if(!lr->islvalue())
					error(this, "Cannot append to an r-value array");

				auto appendf = cgn::glue::array::getAppendFunction(cs, lt->toDynamicArrayType());

				//? are there any ramifications for these actions for ref-counted things?
				auto res = cs->irb.Call(appendf, lr, cs->irb.CreateSliceFromSAA(rr, false));

				cs->irb.Store(res, lr);
				return CGResult(0);
			}
			else if(lt->isDynamicArrayType() && lt->getArrayElementType() == rt)
			{
				// right then.
				if(!lr->islvalue())
					error(this, "Cannot append to an r-value array");

				auto appendf = cgn::glue::array::getElementAppendFunction(cs, lt->toDynamicArrayType());

				//? are there any ramifications for these actions for ref-counted things?
				auto res = cs->irb.Call(appendf, lr, rr);

				cs->irb.Store(res, lr);
				return CGResult(0);
			}
			else if(lt->isStringType() && lt == rt)
			{
				// right then.
				if(!lr->islvalue())
					error(this, "Cannot append to an r-value array");

				auto appendf = cgn::glue::string::getAppendFunction(cs);

				//? are there any ramifications for these actions for ref-counted things?
				auto res = cs->irb.Call(appendf, lr, cs->irb.CreateSliceFromSAA(rr, true));

				cs->irb.Store(res, lr);
				return CGResult(0);
			}
			else if(lt->isStringType() && rt->isCharType())
			{
				// right then.
				if(!lr->islvalue())
					error(this, "Cannot append to an r-value string");

				auto appendf = cgn::glue::string::getCharAppendFunction(cs);

				//? are there any ramifications for these actions for ref-counted things?
				auto res = cs->irb.Call(appendf, lr, rr);

				cs->irb.Store(res, lr);
				return CGResult(0);
			}
		}


		// do the op first
		auto res = cs->performBinaryOperation(this->loc, { this->left->loc, lr }, { this->right->loc, rr }, nonass);

		// assign the res to the thing
		rr = res.value;
	}

	rr = cs->oneWayAutocast(rr, lt);

	if(rr == 0)
	{
		error(this, "Invalid assignment from value of type '%s' to expected type '%s'", rr->getType(), lt);
	}

	// ok then
	if(lt != rr->getType())
		error(this, "What? left = %s, right = %s", lt, rr->getType());

	cs->autoAssignRefCountedValue(lr, rr, /* isInitial: */ false, /* performStore: */ true);
	return CGResult(0);
}