diff --git a/packages/CLPBN/clpbn.yap b/packages/CLPBN/clpbn.yap index c28814f92..92ffc70c4 100644 --- a/packages/CLPBN/clpbn.yap +++ b/packages/CLPBN/clpbn.yap @@ -7,6 +7,7 @@ clpbn_key/2, clpbn_init_solver/4, clpbn_run_solver/3, + clpbn_finalize_solver/1, clpbn_init_solver/5, clpbn_run_solver/4, clpbn_init_graph/1, @@ -35,6 +36,15 @@ run_vel_solver/3 ]). +:- use_module('clpbn/bp', + [bp/3, + check_if_bp_done/1, + init_bp_solver/4, + run_bp_solver/3, + finalize_bp_solver/1 + ]). + + :- use_module('clpbn/jt', [jt/3, init_jt_solver/4, @@ -53,6 +63,14 @@ run_gibbs_solver/3 ]). +:- use_module('clpbn/bp', + [bp/3, + check_if_bp_done/1, + init_bp_solver/4, + run_bp_solver/3, + finalize_bp_solver/1 + ]). + :- use_module('clpbn/pgrammar', [init_pcg_solver/4, run_pcg_solver/3, @@ -229,6 +247,8 @@ write_out(vel, GVars, AVars, DiffVars) :- vel(GVars, AVars, DiffVars). write_out(jt, GVars, AVars, DiffVars) :- jt(GVars, AVars, DiffVars). +write_out(bp, GVars, AVars, DiffVars) :- + bp(GVars, AVars, DiffVars). write_out(gibbs, GVars, AVars, DiffVars) :- gibbs(GVars, AVars, DiffVars). write_out(bnt, GVars, AVars, DiffVars) :- @@ -317,6 +337,9 @@ bind_clpbn(_, Var, _, _, _, _, []) :- bind_clpbn(_, Var, _, _, _, _, []) :- use(vel), check_if_vel_done(Var), !. +bind_clpbn(_, Var, _, _, _, _, []) :- + use(bp), + check_if_bp_done(Var), !. bind_clpbn(_, Var, _, _, _, _, []) :- use(jt), check_if_vel_done(Var), !. @@ -379,18 +402,21 @@ clpbn_key(Var,Key) :- % values at the end of the day. % clpbn_init_solver(LVs, Vs0, VarsWithUnboundKeys, State) :- - solver(Solver), + solver(Solver), clpbn_init_solver(Solver, LVs, Vs0, VarsWithUnboundKeys, State). clpbn_init_solver(gibbs, LVs, Vs0, VarsWithUnboundKeys, State) :- init_gibbs_solver(LVs, Vs0, VarsWithUnboundKeys, State). clpbn_init_solver(vel, LVs, Vs0, VarsWithUnboundKeys, State) :- init_vel_solver(LVs, Vs0, VarsWithUnboundKeys, State). +clpbn_init_solver(bp, LVs, Vs0, VarsWithUnboundKeys, State) :- + init_bp_solver(LVs, Vs0, VarsWithUnboundKeys, State). clpbn_init_solver(jt, LVs, Vs0, VarsWithUnboundKeys, State) :- init_jt_solver(LVs, Vs0, VarsWithUnboundKeys, State). clpbn_init_solver(pcg, LVs, Vs0, VarsWithUnboundKeys, State) :- init_pcg_solver(LVs, Vs0, VarsWithUnboundKeys, State). + % % LVs is the list of lists of variables to marginalise % Vs is the full graph @@ -398,15 +424,21 @@ clpbn_init_solver(pcg, LVs, Vs0, VarsWithUnboundKeys, State) :- % % clpbn_run_solver(LVs, LPs, State) :- - solver(Solver), + solver(Solver), clpbn_run_solver(Solver, LVs, LPs, State). clpbn_run_solver(gibbs, LVs, LPs, State) :- run_gibbs_solver(LVs, LPs, State). + clpbn_run_solver(vel, LVs, LPs, State) :- run_vel_solver(LVs, LPs, State). + +clpbn_run_solver(bp, LVs, LPs, State) :- + run_bp_solver(LVs, LPs, State). + clpbn_run_solver(jt, LVs, LPs, State) :- run_jt_solver(LVs, LPs, State). + clpbn_run_solver(pcg, LVs, LPs, State) :- run_pcg_solver(LVs, LPs, State). @@ -415,3 +447,10 @@ add_keys(Key1+V1,_Key2,Key1+V1). clpbn_init_graph(pcg) :- !, pcg_init_graph. clpbn_init_graph(_). + +clpbn_finalize_solver(State) :- + solver(bp), !, + functor(State, _, Last), + arg(Last, State, Info), + finalize_bp_solver(Info). +clpbn_finalize_solver(_State). diff --git a/packages/CLPBN/clpbn/bp.yap b/packages/CLPBN/clpbn/bp.yap index da4043a4f..34d732453 100644 --- a/packages/CLPBN/clpbn/bp.yap +++ b/packages/CLPBN/clpbn/bp.yap @@ -1,107 +1,94 @@ -/*********************************** +/************************************************ Belief Propagation in CLP(BN) - - This should connect to C-code. -*********************************/ +**************************************************/ -:- module(clpbn_bp, [ - bp/3, - check_if_bp_done/1, - init_bp_solver/4, - run_bp_solver/3]). - - -:- use_module(library('clpbn/aggregates'), - [check_for_agg_vars/2]). - -:- use_module(library('clpbn/connected'), - [init_influences/3, - influences/5 +:- module(clpbn_bp, + [bp/3, + check_if_bp_done/1, + init_bp_solver/4, + run_bp_solver/3, + finalize_bp_solver/1 ]). + :- use_module(library('clpbn/dists'), [dist/4, get_dist_domain/2, + get_dist_domain_size/2, get_dist_params/2 ]). -:- use_module(library('clpbn/display'), - [clpbn_bind_vals/3]). -:-use_module(library(lists), - [append/3, - memberchk/2 - ]). +:- use_module(library('clpbn/display'), + [clpbn_bind_vals/3]). + + +:- use_module(library(atts)). + +:- use_module(library(charsio)). :- load_foreign_files(['horus'], [], init_predicates). -:- attribute all_diffs/1. +:- attribute id/1. + +:- dynamic num_bayes_nets/1. check_if_bp_done(_Var). -% -% implementation of belief propagation -% -% A1 = +QueryVars -> sets of independent marginalization variables -% A2 = *AllVars -> list of all variables -% A3 = -Output -> output probabilities -% -% Other important variables: -% -% State0 initialized graph, is used to pass data from initialization -% to query solving (eg, State might be the JT and be used to run -% different queries). -% +num_bayes_nets(0). + + bp([[]],_,_) :- !. -bp([QueryVars],AllVars,Output) :- - writeln(queryVars:QueryVars), - writeln(allVars:AllVars), - % init_bp_solver([QueryVars], AllVars, Output, State), - run_bp_solver([QueryVars], [AllVars], _State), - % bind probs back to variables so that they can be output. - clpbn_bind_vals([QueryVars],[LPs],Output). - -% initialise necessary data for query solver -init_bp_solver(Qs, AllVars, _, graph(LVis)) :- - % replace average, max, min and friends by binary nodes. - check_for_agg_vars(AllVars, UnFoldedVars), - % replace the variables reachable from G - init_influences(UnfoldedVars, G, RG), - init_bp_solver_for_questions(Qs, G, RG, _, LVis). - -init_bp_solver_for_questions([], _, _, [], []). -init_bp_solver_for_questions([Vs|MVs], G, RG, [NVs|MNVs0], [NVs|LVis]) :- - % find variables connectd to Vs - influences(Vs, _, NVs0, G, RG), - sort(NVs0, NVs), - init_bp_solver_for_questions(MVs, G, RG, MNVs0, LVis). +bp([QueryVars], AllVars, Output) :- + init_bp_solver(_, AllVars, _, BayesNet), + run_bp_solver([QueryVars], LPs, BayesNet), + finalize_bp_solver(BayesNet), + clpbn_bind_vals([QueryVars], LPs, Output). -% use a findall to recover space without needing for GC -run_bp_solver(LVs, LPs, _) :- - findall(Ps, solve_bp(LVs, LPs, Ps), LPs). +init_bp_solver(_, AllVars, _, (BayesNet, DistIds)) :- + %inc_num_bayes_nets, + %(showprofres(50) -> true ; true), + process_ids(AllVars, 0, DistIds0), + get_vars_info(AllVars, VarsInfo), + sort(DistIds0, DistIds), + %(num_bayes_nets(0) -> writeln(vars:VarsInfo) ; true), + %(num_bayes_nets(0) -> writeln(dists:DistsInfo) ; true), + create_network(VarsInfo, BayesNet). + %get_extra_vars_info(AllVars, ExtraVarsInfo), + %set_extra_vars_info(BayesNet, ExtraVarsInfo). + - -solve_bp([LVs|_], [NVs0|_], Ps) :- - get_vars_info(NVs0, LVi), - get_dists_info(NVs0, Dists), - process(LVi, Dists, LVs, Ps). -solve_bp([_|MoreLVs], [_|MoreLVis], Ps) :- - solve_bp(MoreLVs, MoreLVis, Ps). +process_ids([], _, []). +process_ids([V|Vs], VarId0, [DistId|DistIds]) :- + clpbn:get_atts(V, [dist(DistId, _)]), !, + put_atts(V, [id(VarId0)]), + VarId is VarId0 + 1, + process_ids(Vs, VarId, DistIds). +process_ids([_|Vs], VarId, DistIds) :- + process_ids(Vs, VarId, DistIds). get_vars_info([], []). -get_vars_info([V|Vs], [var(V, Id, Parents, NParents, Ev)|LV]) :- - clpbn:get_atts(V, [dist(Id, Parents)]), !, - length(Parents, NParents), +get_vars_info([V|Vs], [var(VarId, DSize, Ev, ParentIds, DistId)|VarsInfo]) :- + clpbn:get_atts(V, [dist(DistId, Parents)]), !, + get_atts(V, [id(VarId)]), + get_dist_domain_size(DistId, DSize), get_evidence(V, Ev), - get_vars_info(Vs, LV). -get_vars_info([_|Vs], LV) :- - get_vars_info(Vs, LV). + vars2ids(Parents, ParentIds), + get_vars_info(Vs, VarsInfo). +get_vars_info([_|Vs], VarsInfo) :- + get_vars_info(Vs, VarsInfo). + + +vars2ids([], []). +vars2ids([V|QueryVars], [VarId|Ids]) :- + get_atts(V, [id(VarId)]), + vars2ids(QueryVars, Ids). get_evidence(V, Ev) :- @@ -109,49 +96,57 @@ get_evidence(V, Ev) :- get_evidence(V, -1). % no evidence !!! -get_dists_info([],[]). -get_dists_info([V|Vs], [dist(Id, Domain, DSize, Params, NParams) | Dists]) :- - clpbn:get_atts(V, [dist(Id, _)]), !, - get_dist_domain(Id, Domain), - length(Domain, DSize), +get_extra_vars_info([], []). +get_extra_vars_info([V|Vs], [v(VarId, Label, Domain)|VarsInfo]) :- + get_atts(V, [id(VarId)]), !, + clpbn:get_atts(V, [key(Key),dist(DistId, _)]), + term_to_atom(Key, Label), + get_dist_domain(DistId, Domain0), + numbers2atoms(Domain0, Domain), + get_extra_vars_info(Vs, VarsInfo). +get_extra_vars_info([_|Vs], VarsInfo) :- + get_extra_vars_info(Vs, VarsInfo). + + +numbers2atoms([], []). +numbers2atoms([Atom|L0], [Atom|L]) :- + atom(Atom), !, + numbers2atoms(L0, L). +numbers2atoms([Number|L0], [Atom|L]) :- + number_atom(Number, Atom), + numbers2atoms(L0, L). + + +run_bp_solver(QVsL0, LPs, (BayesNet, DistIds)) :- + get_dists_parameters(DistIds, DistsParams), + set_parameters(BayesNet, DistsParams), + process_query_list(QVsL0, QVsL), + %writeln(' qvs':QVsL), + %(num_bayes_nets(1506) -> writeln(qvs:QVsL) ; true), + run_solver(BayesNet, QVsL, LPs). + + +process_query_list([], []). +process_query_list([[V]|QueryVars], [VarId|Ids]) :- !, + get_atts(V, [id(VarId)]), + process_query_list(QueryVars, Ids). +process_query_list([Vs|QueryVars], [VarIds|Ids]) :- + vars2ids(Vs, VarIds), + process_query_list(QueryVars, Ids). + + +get_dists_parameters([],[]). +get_dists_parameters([Id|Ids], [dist(Id, Params)|DistsInfo]) :- get_dist_params(Id, Params), - length(Params, NParams), - get_dists_info(Vs, Dists). -get_dists_info([_|Vs], Dists) :- - get_dists_info(Vs, Dists). + get_dists_parameters(Ids, DistsInfo). -% +Vars is a list containing info about every clpbn variables -% +Dists is a list containing info about distributions -% +QVs is a list containing the query variables -% -Out is some output term stating the probabilities -process(Vars, Dists, QVs, Out) :- - write('vars = '), writeln(Vars), - order_vars(Vars, [], OrderedVars), - write('ovars = '), writeln(OrderedVars), - write('dists = '), writeln(Dists), - write('qvs = '), writeln(QVs), - length(OrderedVars, NVars), - length(Dists, NDists), - %create_network(OrderedVars, NVars, Dists, NDists, BNet), - length(QVs, NQVs), - run_solver(BNet, QVs, NQVs, Out), - free_memory(BNet). +finalize_bp_solver((BayesNet, _)) :- + delete_bayes_net(BayesNet). -order_vars([V], _, [V]) :- !. -order_vars([var(V, Id, Parents, NParents, Ev)|Vs], ParsedVars, [var(V, Id, Parents, NParents, Ev)|OrderedVars]) :- - \+ memberchk(V, ParsedVars), - parents_defined(Parents, ParsedVars), !, - order_vars(Vs, [V|ParsedVars], OrderedVars). -order_vars([var(V, Id, Parents, NParents, Ev)|Vs], ParsedVars, OrderedVars) :- - append(Vs, [var(V, Id, Parents, NParents, Ev)], NVs), - order_vars(NVs, ParsedVars, OrderedVars). +inc_num_bayes_nets :- + retract(num_bayes_nets(Count0)), + Count is Count0 + 1, + assert(num_bayes_nets(Count)). - -parents_defined([], _) :- !. -parents_defined([Parent|Parents], ParsedVars) :- - memberchk(Parent, ParsedVars), - parents_defined(Parents, ParsedVars). - -% f(F), b(B). ----> FAIL diff --git a/packages/CLPBN/clpbn/bp/BPSolver.cpp b/packages/CLPBN/clpbn/bp/BPSolver.cpp new file mode 100755 index 000000000..1d3566aa8 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/BPSolver.cpp @@ -0,0 +1,891 @@ +#include +#include +#include +#include +#include +#include + +#include "BPSolver.h" +#include "BpNode.h" + + +BPSolver* Edge::klass = 0; +StatisticMap Statistics::stats_; +unsigned Statistics::numCreatedNets = 0; +unsigned Statistics::numSolvedPolyTrees = 0; +unsigned Statistics::numSolvedLoopyNets = 0; +unsigned Statistics::numUnconvergedRuns = 0; +unsigned Statistics::maxIterations = 0; +unsigned Statistics::totalOfIterations = 0; + + +BPSolver::BPSolver (const BayesNet& bn) : Solver (&bn) +{ + bn_ = &bn; + forceGenericSolver_ = false; + //forceGenericSolver_ = true; + schedule_ = S_SEQ_FIXED; + //schedule_ = S_SEQ_RANDOM; + //schedule_ = S_PARALLEL; + //schedule_ = S_MAX_RESIDUAL; + maxIter_ = 205; + accuracy_ = 0.000001; +} + + + +BPSolver::~BPSolver (void) +{ + for (unsigned i = 0; i < msgs_.size(); i++) { + delete msgs_[i]; + } +} + + + +void +BPSolver::runSolver (void) +{ + if (DL >= 1) { + //bn_->printNetwork(); + } + + clock_t start_ = clock(); + if (bn_->isSingleConnected() && !forceGenericSolver_) { + runPolyTreeSolver(); + Statistics::numSolvedPolyTrees ++; + } else { + runGenericSolver(); + Statistics::numSolvedLoopyNets ++; + if (nIter_ >= maxIter_) { + Statistics::numUnconvergedRuns ++; + } else { + Statistics::updateIterations (nIter_); + } + if (DL >= 1) { + cout << endl; + if (nIter_ < maxIter_) { + cout << "Belief propagation converged in " ; + cout << nIter_ << " iterations" << endl; + } else { + cout << "The maximum number of iterations was hit, terminating..." ; + cout << endl; + } + } + } + double time = (double (clock() - start_)) / CLOCKS_PER_SEC; + unsigned size = bn_->getNumberOfNodes(); + Statistics::updateStats (size, time); + //if (size > 30) { + // stringstream ss; + // ss << size << "." << Statistics::getCounting (size) << ".dot" ; + // bn_->exportToDotFile (ss.str().c_str()); + //} +} + + + +ParamSet +BPSolver::getPosterioriOf (const Variable* var) const +{ + assert (var); + assert (var == bn_->getNode (var->getVarId())); + assert (var->getIndex() < msgs_.size()); + return msgs_[var->getIndex()]->getBeliefs(); +} + + + + +ParamSet +BPSolver::getJointDistribution (const NodeSet& jointVars) const +{ + if (DL >= 1) { + cout << "calculating joint distribuition on: " ; + for (unsigned i = 0; i < jointVars.size(); i++) { + cout << jointVars[i]->getLabel() << " " ; + } + cout << endl; + } + + //BayesNet* workingNet = bn_->pruneNetwork (bn_->getNodes()); + //FIXME see if this works: + BayesNet* workingNet = bn_->pruneNetwork (jointVars); + BayesNode* node = workingNet->getNode (jointVars[0]->getVarId()); + + BayesNet* tempNet = workingNet->pruneNetwork (node); + BPSolver solver (*tempNet); + solver.runSolver(); + + NodeSet observedVars = { jointVars[0] }; + + node = tempNet->getNode (jointVars[0]->getVarId()); + ParamSet prevBeliefs = solver.getPosterioriOf (node); + + delete tempNet; + + for (unsigned i = 1; i < jointVars.size(); i++) { + node = workingNet->getNode (observedVars[i - 1]->getVarId()); + if (!node->hasEvidence()) { + node->setEvidence (0); + } + node = workingNet->getNode (jointVars[i]->getVarId()); + tempNet = workingNet->pruneNetwork (node); + + ParamSet allBeliefs; + vector confs = + BayesNet::getDomainConfigurationsOf (observedVars); + for (unsigned j = 0; j < confs.size(); j++) { + for (unsigned k = 0; k < observedVars.size(); k++) { + node = tempNet->getNode (observedVars[k]->getVarId()); + if (!observedVars[k]->hasEvidence()) { + if (node) { + node->setEvidence (confs[j][k]); + } else { + // FIXME try optimize + //assert (false); + cout << observedVars[k]->getLabel(); + cout << " is not in temporary net!" ; + cout << endl; + } + } else { + cout << observedVars[k]->getLabel(); + cout << " already has evidence in original net!" ; + cout << endl; + } + } + BPSolver solver (*tempNet); + node = tempNet->getNode (jointVars[i]->getVarId()); + solver.runSolver(); + ParamSet beliefs = solver.getPosterioriOf (node); + for (unsigned k = 0; k < beliefs.size(); k++) { + allBeliefs.push_back (beliefs[k]); + } + } + + int count = -1; + for (unsigned j = 0; j < allBeliefs.size(); j++) { + if (j % jointVars[i]->getDomainSize() == 0) { + count ++; + } + allBeliefs[j] *= prevBeliefs[count]; + } + prevBeliefs = allBeliefs; + observedVars.push_back (jointVars[i]); + delete tempNet; + } + delete workingNet; + return prevBeliefs; +} + + + +void +BPSolver::initializeSolver (void) +{ + if (DL >= 1) { + cout << "Initializing solver" << endl; + cout << "-> schedule = "; + if (forceGenericSolver_) { + switch (schedule_) { + case S_SEQ_FIXED: cout << "sequential fixed" ; break; + case S_SEQ_RANDOM: cout << "sequential random" ; break; + case S_PARALLEL: cout << "parallel" ; break; + case S_MAX_RESIDUAL: cout << "max residual" ; break; + } + } else { + cout << "polytree solver" ; + } + cout << endl; + cout << "-> max iters = " << maxIter_ << endl; + cout << "-> accuracy = " << accuracy_ << endl; + cout << endl; + } + + const NodeSet& nodes = bn_->getNodes(); + for (unsigned i = 0; i < msgs_.size(); i++) { + delete msgs_[i]; + } + msgs_.clear(); + msgs_.reserve (nodes.size()); + updateOrder_.clear(); + sortedOrder_.clear(); + edgeMap_.clear(); + + for (unsigned i = 0; i < nodes.size(); i++) { + msgs_.push_back (new BpNode (nodes[i])); + } + + NodeSet roots = bn_->getRootNodes(); + for (unsigned i = 0; i < roots.size(); i++) { + const ParamSet& params = roots[i]->getParameters(); + ParamSet& piVals = M(roots[i])->getPiValues(); + for (int ri = 0; ri < roots[i]->getDomainSize(); ri++) { + piVals[ri] = params[ri]; + } + } +} + + + +void +BPSolver::incorporateEvidence (BayesNode* x) +{ + ParamSet& piVals = M(x)->getPiValues(); + ParamSet& ldVals = M(x)->getLambdaValues(); + for (int xi = 0; xi < x->getDomainSize(); xi++) { + piVals[xi] = 0.0; + ldVals[xi] = 0.0; + } + piVals[x->getEvidence()] = 1.0; + ldVals[x->getEvidence()] = 1.0; +} + + + +void +BPSolver::runPolyTreeSolver (void) +{ + initializeSolver(); + const NodeSet& nodes = bn_->getNodes(); + + // Hack: I need this else this can happen with bayes ball + // Variable: 174 + // Id: 174 + // Domain: -1, 0, 1 + // Evidence: 1 + // Parents: + // Childs: 176 + // cpt + // ---------------------------------------------------- + // -1 0 0 0 0 ... + // 0 0.857143 0.857143 0.857143 0.857143 ... + // 1 0.142857 0.142857 0.142857 0.142857 ... + // the cpt for this node would be 0,0,0 + + for (unsigned i = 0; i < nodes.size(); i++) { + if (nodes[i]->hasEvidence()) { + incorporateEvidence (nodes[i]); + } + } + + // first compute all node marginals ... + NodeSet roots = bn_->getRootNodes(); + for (unsigned i = 0; i < roots.size(); i++) { + const NodeSet& childs = roots[i]->getChilds(); + for (unsigned j = 0; j < childs.size(); j++) { + polyTreePiMessage (roots[i], childs[j]); + } + } + // then propagate the evidence + for (unsigned i = 0; i < nodes.size(); i++) { + if (nodes[i]->hasEvidence()) { + incorporateEvidence (nodes[i]); + const NodeSet& parents = nodes[i]->getParents(); + for (unsigned j = 0; j < parents.size(); j++) { + if (!parents[j]->hasEvidence()) { + polyTreeLambdaMessage (nodes[i], parents[j]); + } + } + const NodeSet& childs = nodes[i]->getChilds(); + for (unsigned j = 0; j < childs.size(); j++) { + polyTreePiMessage (nodes[i], childs[j]); + } + } + } +} + + + +void +BPSolver::polyTreePiMessage (BayesNode* z, BayesNode* x) +{ + if (DL >= 1) { + cout << PI << " (" << z->getLabel(); + cout << " --> " << x->getLabel(); + cout << ")" << endl; + } + calculateNextPiMessage (z, x); + updatePiMessage (z, x); + + if (!x->hasEvidence()) { + updatePiValues (x); + const NodeSet& xChilds = x->getChilds(); + for (unsigned i = 0; i < xChilds.size(); i++) { + polyTreePiMessage (x, xChilds[i]); + } + } + + if (M(x)->hasReceivedChildInfluence()) { + const NodeSet& xParents = x->getParents(); + for (unsigned i = 0; i < xParents.size(); i++) { + if (xParents[i] != z && !xParents[i]->hasEvidence()) { + polyTreeLambdaMessage (x, xParents[i]); + } + } + } +} + + + +void +BPSolver::polyTreeLambdaMessage (BayesNode* y, BayesNode* x) +{ + if (DL >= 1) { + cout << LD << " (" << y->getLabel(); + cout << " --> " << x->getLabel(); + cout << ")" << endl; + } + calculateNextLambdaMessage (y, x); + updateLambdaMessage (y, x); + updateLambdaValues (x); + + const NodeSet& xParents = x->getParents(); + for (unsigned i = 0; i < xParents.size(); i++) { + if (!xParents[i]->hasEvidence()) { + polyTreeLambdaMessage (x, xParents[i]); + } + } + + const NodeSet& xChilds = x->getChilds(); + for (unsigned i = 0; i < xChilds.size(); i++) { + if (xChilds[i] != y) { + polyTreePiMessage (x, xChilds[i]); + } + } +} + + + +void +BPSolver::runGenericSolver() +{ + initializeSolver(); + const NodeSet& nodes = bn_->getNodes(); + for (unsigned i = 0; i < nodes.size(); i++) { + if (nodes[i]->hasEvidence()) { + incorporateEvidence (nodes[i]); + } + } + + for (unsigned i = 0; i < nodes.size(); i++) { + // pi messages + const NodeSet& childs = nodes[i]->getChilds(); + for (unsigned j = 0; j < childs.size(); j++) { + updateOrder_.push_back (Edge (nodes[i], childs[j], PI_MSG)); + } + // lambda messages + const NodeSet& parents = nodes[i]->getParents(); + for (unsigned j = 0; j < parents.size(); j++) { + if (!parents[j]->hasEvidence()) { + updateOrder_.push_back (Edge (nodes[i], parents[j], LAMBDA_MSG)); + } + } + } + + nIter_ = 0; + while (!converged() && nIter_ < maxIter_) { + + nIter_++; + if (DL >= 1) { + cout << endl; + cout << "****************************************" ; + cout << "****************************************" ; + cout << endl; + cout << " Iteration " << nIter_ << endl; + cout << "****************************************" ; + cout << "****************************************" ; + cout << endl; + } + + switch (schedule_) { + + case S_SEQ_RANDOM: + random_shuffle (updateOrder_.begin(), updateOrder_.end()); + // no break + + case S_SEQ_FIXED: + for (unsigned i = 0; i < updateOrder_.size(); i++) { + calculateNextMessage (updateOrder_[i]); + updateMessage (updateOrder_[i]); + updateValues (updateOrder_[i]); + } + break; + + case S_PARALLEL: + for (unsigned i = 0; i < updateOrder_.size(); i++) { + calculateNextMessage (updateOrder_[i]); + } + for (unsigned i = 0; i < updateOrder_.size(); i++) { + updateMessage (updateOrder_[i]); + updateValues (updateOrder_[i]); + } + break; + + case S_MAX_RESIDUAL: + maxResidualSchedule(); + break; + + } + } +} + + + +void +BPSolver::maxResidualSchedule (void) +{ + if (nIter_ == 1) { + Edge::klass = this; + for (unsigned i = 0; i < updateOrder_.size(); i++) { + calculateNextMessage (updateOrder_[i]); + updateResidual (updateOrder_[i]); + SortedOrder::iterator it = sortedOrder_.insert (updateOrder_[i]); + edgeMap_.insert (make_pair (updateOrder_[i].getId(), it)); + } + return; + } + + for (unsigned c = 0; c < sortedOrder_.size(); c++) { + if (DL >= 1) { + for (set::iterator it = sortedOrder_.begin(); + it != sortedOrder_.end(); it ++) { + cout << it->toString() << " residual = " ; + cout << getResidual (*it) << endl; + } + } + + set::iterator it = sortedOrder_.begin(); + Edge e = *it; + if (getResidual (e) < accuracy_) { + return; + } + updateMessage (e); + updateValues (e); + clearResidual (e); + sortedOrder_.erase (it); + assert (edgeMap_.find (e.getId()) != edgeMap_.end()); + edgeMap_.find (e.getId())->second = sortedOrder_.insert (e); + + // update the messages that depend on message source --> destination + const NodeSet& childs = e.destination->getChilds(); + for (unsigned i = 0; i < childs.size(); i++) { + if (childs[i] != e.source) { + Edge neighbor (e.destination, childs[i], PI_MSG); + calculateNextMessage (neighbor); + updateResidual (neighbor); + assert (edgeMap_.find (neighbor.getId()) != edgeMap_.end()); + EdgeMap::iterator iter = edgeMap_.find (neighbor.getId()); + sortedOrder_.erase (iter->second); + iter->second = sortedOrder_.insert (neighbor); + } + } + const NodeSet& parents = e.destination->getParents(); + for (unsigned i = 0; i < parents.size(); i++) { + if (parents[i] != e.source && !parents[i]->hasEvidence()) { + Edge neighbor (e.destination, parents[i], LAMBDA_MSG); + calculateNextMessage (neighbor); + updateResidual (neighbor); + assert (edgeMap_.find (neighbor.getId()) != edgeMap_.end()); + EdgeMap::iterator iter = edgeMap_.find (neighbor.getId()); + sortedOrder_.erase (iter->second); + iter->second = sortedOrder_.insert (neighbor); + } + } + } +} + + + +bool +BPSolver::converged (void) const +{ + bool converged = true; + if (schedule_ == S_MAX_RESIDUAL) { + if (nIter_ <= 2) { + return false; + } + // this can happen if every node does not have neighbors + if (sortedOrder_.size() == 0) { + return true; + } + Param maxResidual = getResidual (*(sortedOrder_.begin())); + if (maxResidual > accuracy_) { + return false; + } + } else { + if (nIter_ == 0) { + return false; + } + const NodeSet& nodes = bn_->getNodes(); + for (unsigned i = 0; i < nodes.size(); i++) { + if (!nodes[i]->hasEvidence()) { + double change = M(nodes[i])->getBeliefChange(); + if (DL >= 1) { + cout << nodes[i]->getLabel() + " belief change = " ; + cout << change << endl; + } + if (change > accuracy_) { + converged = false; + if (DL == 0) break; + } + } + } + } + + return converged; +} + + + +void +BPSolver::updatePiValues (BayesNode* x) +{ + // π(Xi) + const NodeSet& parents = x->getParents(); + const vector& entries = x->getCptEntries(); + assert (parents.size() != 0); + stringstream* calcs1; + stringstream* calcs2; + + ParamSet messageProducts (entries.size()); + for (unsigned k = 0; k < entries.size(); k++) { + if (DL >= 5) { + calcs1 = new stringstream; + calcs2 = new stringstream; + } + double messageProduct = 1.0; + const DomainConf& conf = entries[k].getParentConfigurations(); + for (unsigned i = 0; i < parents.size(); i++) { + messageProduct *= M(parents[i])->getPiMessageValue(x, conf[i]); + if (DL >= 5) { + if (i != 0) *calcs1 << "." ; + if (i != 0) *calcs2 << "*" ; + *calcs1 << PI << "(" << x->getLabel() << ")" ; + *calcs1 << "[" << parents[i]->getDomain()[conf[i]] << "]"; + *calcs2 << M(parents[i])->getPiMessageValue(x, conf[i]); + } + } + messageProducts[k] = messageProduct; + if (DL >= 5) { + cout << " mp" << k; + cout << " = " << (*calcs1).str(); + if (parents.size() == 1) { + cout << " = " << messageProduct << endl; + } else { + cout << " = " << (*calcs2).str(); + cout << " = " << messageProduct << endl; + } + delete calcs1; + delete calcs2; + } + } + + for (int xi = 0; xi < x->getDomainSize(); xi++) { + double sum = 0.0; + if (DL >= 5) { + calcs1 = new stringstream; + calcs2 = new stringstream; + } + for (unsigned k = 0; k < entries.size(); k++) { + sum += x->getProbability (xi, entries[k]) * messageProducts[k]; + if (DL >= 5) { + if (k != 0) *calcs1 << " + " ; + if (k != 0) *calcs2 << " + " ; + *calcs1 << x->cptEntryToString (xi, entries[k]); + *calcs1 << ".mp" << k; + *calcs2 << x->getProbability (xi, entries[k]); + *calcs2 << "*" << messageProducts[k]; + } + } + M(x)->setPiValue (xi, sum); + if (DL >= 5) { + cout << " " << PI << "(" << x->getLabel() << ")" ; + cout << "[" << x->getDomain()[xi] << "]" ; + cout << " = " << (*calcs1).str(); + cout << " = " << (*calcs2).str(); + cout << " = " << sum << endl; + delete calcs1; + delete calcs2; + } + } +} + + + +void +BPSolver::updateLambdaValues (BayesNode* x) +{ + // λ(Xi) + const NodeSet& childs = x->getChilds(); + assert (childs.size() != 0); + stringstream* calcs1; + stringstream* calcs2; + + for (int xi = 0; xi < x->getDomainSize(); xi++) { + double product = 1.0; + if (DL >= 5) { + calcs1 = new stringstream; + calcs2 = new stringstream; + } + for (unsigned i = 0; i < childs.size(); i++) { + product *= M(x)->getLambdaMessageValue(childs[i], xi); + if (DL >= 5) { + if (i != 0) *calcs1 << "." ; + if (i != 0) *calcs2 << "*" ; + *calcs1 << LD << "(" << childs[i]->getLabel(); + *calcs1 << "-->" << x->getLabel() << ")" ; + *calcs1 << "[" << x->getDomain()[xi] << "]" ; + *calcs2 << M(x)->getLambdaMessageValue(childs[i], xi); + } + } + M(x)->setLambdaValue (xi, product); + if (DL >= 5) { + cout << " " << LD << "(" << x->getLabel() << ")" ; + cout << "[" << x->getDomain()[xi] << "]" ; + cout << " = " << (*calcs1).str(); + if (childs.size() == 1) { + cout << " = " << product << endl; + } else { + cout << " = " << (*calcs2).str(); + cout << " = " << product << endl; + } + delete calcs1; + delete calcs2; + } + } +} + + + +void +BPSolver::calculateNextPiMessage (BayesNode* z, BayesNode* x) +{ + // πX(Zi) + ParamSet& zxPiNextMessage = M(z)->piNextMessageReference (x); + const NodeSet& zChilds = z->getChilds(); + stringstream* calcs1; + stringstream* calcs2; + + for (int zi = 0; zi < z->getDomainSize(); zi++) { + double product = M(z)->getPiValue (zi); + if (DL >= 5) { + calcs1 = new stringstream; + calcs2 = new stringstream; + *calcs1 << PI << "(" << z->getLabel() << ")"; + *calcs1 << "[" << z->getDomain()[zi] << "]" ; + *calcs2 << product; + } + for (unsigned i = 0; i < zChilds.size(); i++) { + if (zChilds[i] != x) { + product *= M(z)->getLambdaMessageValue(zChilds[i], zi); + if (DL >= 5) { + *calcs1 << "." << LD << "(" << zChilds[i]->getLabel(); + *calcs1 << "-->" << z->getLabel() << ")"; + *calcs1 << "[" << z->getDomain()[zi] + "]" ; + *calcs2 << " * " << M(z)->getLambdaMessageValue(zChilds[i], zi); + } + } + } + zxPiNextMessage[zi] = product; + if (DL >= 5) { + cout << " " << PI << "(" << z->getLabel(); + cout << "-->" << x->getLabel() << ")" ; + cout << "[" << z->getDomain()[zi] << "]" ; + cout << " = " << (*calcs1).str(); + if (zChilds.size() == 1) { + cout << " = " << product << endl; + } else { + cout << " = " << (*calcs2).str(); + cout << " = " << product << endl; + } + delete calcs1; + delete calcs2; + } + } +} + + + +void +BPSolver::calculateNextLambdaMessage (BayesNode* y, BayesNode* x) +{ + // λY(Xi) + //if (!y->hasEvidence() && !M(y)->hasReceivedChildInfluence()) { + // if (DL >= 5) { + // cout << "unnecessary calculation" << endl; + // } + // return; + //} + ParamSet& yxLambdaNextMessage = M(x)->lambdaNextMessageReference (y); + const NodeSet& yParents = y->getParents(); + const vector& allEntries = y->getCptEntries(); + int parentIndex = y->getIndexOfParent (x); + stringstream* calcs1; + stringstream* calcs2; + + vector entries; + DomainConstr constr = make_pair (parentIndex, 0); + for (unsigned i = 0; i < allEntries.size(); i++) { + if (allEntries[i].matchConstraints(constr)) { + entries.push_back (allEntries[i]); + } + } + + ParamSet messageProducts (entries.size()); + for (unsigned k = 0; k < entries.size(); k++) { + if (DL >= 5) { + calcs1 = new stringstream; + calcs2 = new stringstream; + } + double messageProduct = 1.0; + const DomainConf& conf = entries[k].getParentConfigurations(); + for (unsigned i = 0; i < yParents.size(); i++) { + if (yParents[i] != x) { + if (DL >= 5) { + if (messageProduct != 1.0) *calcs1 << "*" ; + if (messageProduct != 1.0) *calcs2 << "*" ; + *calcs1 << PI << "(" << yParents[i]->getLabel(); + *calcs1 << "-->" << y->getLabel() << ")" ; + *calcs1 << "[" << yParents[i]->getDomain()[conf[i]] << "]" ; + *calcs2 << M(yParents[i])->getPiMessageValue(y, conf[i]); + } + messageProduct *= M(yParents[i])->getPiMessageValue(y, conf[i]); + } + } + messageProducts[k] = messageProduct; + if (DL >= 5) { + cout << " mp" << k; + cout << " = " << (*calcs1).str(); + if (yParents.size() == 1) { + cout << 1 << endl; + } else if (yParents.size() == 2) { + cout << " = " << messageProduct << endl; + } else { + cout << " = " << (*calcs2).str(); + cout << " = " << messageProduct << endl; + } + delete calcs1; + delete calcs2; + } + } + + for (int xi = 0; xi < x->getDomainSize(); xi++) { + if (DL >= 5) { + calcs1 = new stringstream; + calcs2 = new stringstream; + } + vector entries; + DomainConstr constr = make_pair (parentIndex, xi); + for (unsigned i = 0; i < allEntries.size(); i++) { + if (allEntries[i].matchConstraints(constr)) { + entries.push_back (allEntries[i]); + } + } + double outerSum = 0.0; + for (int yi = 0; yi < y->getDomainSize(); yi++) { + if (DL >= 5) { + (yi != 0) ? *calcs1 << " + {" : *calcs1 << "{" ; + (yi != 0) ? *calcs2 << " + {" : *calcs2 << "{" ; + } + double innerSum = 0.0; + for (unsigned k = 0; k < entries.size(); k++) { + if (DL >= 5) { + if (k != 0) *calcs1 << " + " ; + if (k != 0) *calcs2 << " + " ; + *calcs1 << y->cptEntryToString (yi, entries[k]); + *calcs1 << ".mp" << k; + *calcs2 << y->getProbability (yi, entries[k]); + *calcs2 << "*" << messageProducts[k]; + } + innerSum += y->getProbability (yi, entries[k]) * messageProducts[k]; + } + outerSum += innerSum * M(y)->getLambdaValue (yi); + if (DL >= 5) { + *calcs1 << "}." << LD << "(" << y->getLabel() << ")" ; + *calcs1 << "[" << y->getDomain()[yi] << "]"; + *calcs2 << "}*" << M(y)->getLambdaValue (yi); + } + } + yxLambdaNextMessage[xi] = outerSum; + if (DL >= 5) { + cout << " " << LD << "(" << y->getLabel(); + cout << "-->" << x->getLabel() << ")" ; + cout << "[" << x->getDomain()[xi] << "]" ; + cout << " = " << (*calcs1).str(); + cout << " = " << (*calcs2).str(); + cout << " = " << outerSum << endl; + delete calcs1; + delete calcs2; + } + } +} + + + +void +BPSolver::printMessageStatusOf (const BayesNode* var) const +{ + cout << left; + cout << setw (10) << "domain" ; + cout << setw (20) << PI << "(" + var->getLabel() + ")" ; + cout << setw (20) << LD << "(" + var->getLabel() + ")" ; + cout << setw (16) << "belief" ; + cout << endl; + cout << "--------------------------------" ; + cout << "--------------------------------" ; + cout << endl; + + BpNode* x = M(var); + ParamSet& piVals = x->getPiValues(); + ParamSet& ldVals = x->getLambdaValues(); + ParamSet beliefs = x->getBeliefs(); + const Domain& domain = var->getDomain(); + const NodeSet& childs = var->getChilds(); + + for (int xi = 0; xi < var->getDomainSize(); xi++) { + cout << setw (10) << domain[xi]; + cout << setw (19) << piVals[xi]; + cout << setw (19) << ldVals[xi]; + cout.precision (PRECISION); + cout << setw (16) << beliefs[xi]; + cout << endl; + } + cout << endl; + if (childs.size() > 0) { + string s = "(" + var->getLabel() + ")" ; + for (unsigned j = 0; j < childs.size(); j++) { + cout << setw (10) << "domain" ; + cout << setw (28) << PI + childs[j]->getLabel() + s; + cout << setw (28) << LD + childs[j]->getLabel() + s; + cout << endl; + cout << "--------------------------------" ; + cout << "--------------------------------" ; + cout << endl; + const ParamSet& piMessage = x->getPiMessage (childs[j]); + const ParamSet& lambdaMessage = x->getLambdaMessage (childs[j]); + for (int xi = 0; xi < var->getDomainSize(); xi++) { + cout << setw (10) << domain[xi]; + cout.precision (PRECISION); + cout << setw (27) << piMessage[xi]; + cout.precision (PRECISION); + cout << setw (27) << lambdaMessage[xi]; + cout << endl; + } + cout << endl; + } + } +} + + + +void +BPSolver::printAllMessageStatus (void) const +{ + const NodeSet& nodes = bn_->getNodes(); + for (unsigned i = 0; i < nodes.size(); i++) { + printMessageStatusOf (nodes[i]); + } +} + + diff --git a/packages/CLPBN/clpbn/bp/BPSolver.h b/packages/CLPBN/clpbn/bp/BPSolver.h new file mode 100755 index 000000000..b752b92d2 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/BPSolver.h @@ -0,0 +1,450 @@ +#ifndef BP_BPSOLVER_H +#define BP_BPSOLVER_H + +#include +#include +#include + +#include "Solver.h" +#include "BayesNet.h" +#include "BpNode.h" +#include "Shared.h" + +using namespace std; + +class BPSolver; + +static const string PI = "pi" ; +static const string LD = "ld" ; + +enum MessageType {PI_MSG, LAMBDA_MSG}; + +class BPSolver; +struct Edge +{ + Edge (BayesNode* s, BayesNode* d, MessageType t) + { + source = s; + destination = d; + type = t; + } + string getId (void) const + { + stringstream ss; + type == PI_MSG ? ss << PI : ss << LD; + ss << source->getVarId() << "." << destination->getVarId(); + return ss.str(); + } + string toString (void) const + { + stringstream ss; + type == PI_MSG ? ss << PI << "(" : ss << LD << "(" ; + ss << source->getLabel() << " --> " ; + ss << destination->getLabel(); + ss << ")" ; + return ss.str(); + } + BayesNode* source; + BayesNode* destination; + MessageType type; + static BPSolver* klass; +}; + + + +/* +class BPMessage +{ + BPMessage (BayesNode* parent, BayesNode* child) + { + parent_ = parent; + child_ = child; + currPiMsg_.resize (child->getDomainSize(), 1); + currLdMsg_.resize (parent->getDomainSize(), 1); + nextLdMsg_.resize (parent->getDomainSize(), 1); + nextPiMsg_.resize (child->getDomainSize(), 1); + piResidual_ = 1.0; + ldResidual_ = 1.0; + } + + Param getPiMessageValue (int idx) const + { + assert (idx >=0 && idx < child->getDomainSize()); + return currPiMsg_[idx]; + } + + Param getLambdaMessageValue (int idx) const + { + assert (idx >=0 && idx < parent->getDomainSize()); + return currLdMsg_[idx]; + } + + const ParamSet& getPiMessage (void) const + { + return currPiMsg_; + } + + const ParamSet& getLambdaMessage (void) const + { + return currLdMsg_; + } + + ParamSet& piNextMessageReference (void) + { + return nextPiMsg_; + } + + ParamSet& lambdaNextMessageReference (const BayesNode* source) + { + return nextLdMsg_; + } + + void updatePiMessage (void) + { + currPiMsg_ = nextPiMsg_; + Util::normalize (currPiMsg_); + } + + void updateLambdaMessage (void) + { + currLdMsg_ = nextLdMsg_; + Util::normalize (currLdMsg_); + } + + double getPiResidual (void) + { + return piResidual_; + } + + double getLambdaResidual (void) + { + return ldResidual_; + } + + void updatePiResidual (void) + { + piResidual_ = Util::getL1dist (currPiMsg_, nextPiMsg_); + } + + void updateLambdaResidual (void) + { + ldResidual_ = Util::getL1dist (currLdMsg_, nextLdMsg_); + } + + void clearPiResidual (void) + { + piResidual_ = 0.0; + } + + void clearLambdaResidual (void) + { + ldResidual_ = 0.0; + } + + BayesNode* parent_; + BayesNode* child_; + ParamSet currPiMsg_; // current pi messages + ParamSet currLdMsg_; // current lambda messages + ParamSet nextPiMsg_; + ParamSet nextLdMsg_; + Param piResidual_; + Param ldResidual_; +}; + + + +class NodeInfo +{ + NodeInfo (BayesNode* node) + { + node_ = node; + piVals_.resize (node->getDomainSize(), 1); + ldVals_.resize (node->getDomainSize(), 1); + } + + ParamSet getBeliefs (void) const + { + double sum = 0.0; + ParamSet beliefs (node_->getDomainSize()); + for (int xi = 0; xi < node_->getDomainSize(); xi++) { + double prod = piVals_[xi] * ldVals_[xi]; + beliefs[xi] = prod; + sum += prod; + } + assert (sum); + //normalize the beliefs + for (int xi = 0; xi < node_->getDomainSize(); xi++) { + beliefs[xi] /= sum; + } + return beliefs; + } + + double getPiValue (int idx) const + { + assert (idx >=0 && idx < node_->getDomainSize()); + return piVals_[idx]; + } + + void setPiValue (int idx, double value) + { + assert (idx >=0 && idx < node_->getDomainSize()); + piVals_[idx] = value; + } + + double getLambdaValue (int idx) const + { + assert (idx >=0 && idx < node_->getDomainSize()); + return ldVals_[idx]; + } + + void setLambdaValue (int idx, double value) + { + assert (idx >=0 && idx < node_->getDomainSize()); + ldVals_[idx] = value; + } + + ParamSet& getPiValues (void) + { + return piVals_; + } + + ParamSet& getLambdaValues (void) + { + return ldVals_; + } + + double getBeliefChange (void) + { + double change = 0.0; + if (oldBeliefs_.size() == 0) { + oldBeliefs_ = getBeliefs(); + change = MAX_CHANGE_; + } else { + ParamSet currentBeliefs = getBeliefs(); + for (int xi = 0; xi < node_->getDomainSize(); xi++) { + change += abs (currentBeliefs[xi] - oldBeliefs_[xi]); + } + oldBeliefs_ = currentBeliefs; + } + return change; + } + + bool hasReceivedChildInfluence (void) const + { + // if all lambda values are equal, then neither + // this node neither its descendents have evidence, + // we can use this to don't send lambda messages his parents + bool childInfluenced = false; + for (int xi = 1; xi < node_->getDomainSize(); xi++) { + if (ldVals_[xi] != ldVals_[0]) { + childInfluenced = true; + break; + } + } + return childInfluenced; + } + + BayesNode* node_; + ParamSet piVals_; // pi values + ParamSet ldVals_; // lambda values + ParamSet oldBeliefs_; +}; +*/ + + +bool compareResidual (const Edge&, const Edge&); + +class BPSolver : public Solver +{ + public: + BPSolver (const BayesNet&); + ~BPSolver (void); + + void runSolver (void); + ParamSet getPosterioriOf (const Variable* var) const; + ParamSet getJointDistribution (const NodeSet&) const; + + private: + DISALLOW_COPY_AND_ASSIGN (BPSolver); + + void initializeSolver (void); + void incorporateEvidence (BayesNode*); + void runPolyTreeSolver (void); + void polyTreePiMessage (BayesNode*, BayesNode*); + void polyTreeLambdaMessage (BayesNode*, BayesNode*); + void runGenericSolver (void); + void maxResidualSchedule (void); + bool converged (void) const; + void updatePiValues (BayesNode*); + void updateLambdaValues (BayesNode*); + void calculateNextPiMessage (BayesNode*, BayesNode*); + void calculateNextLambdaMessage (BayesNode*, BayesNode*); + void printMessageStatusOf (const BayesNode*) const; + void printAllMessageStatus (void) const; + // inlines + void updatePiMessage (BayesNode*, BayesNode*); + void updateLambdaMessage (BayesNode*, BayesNode*); + void calculateNextMessage (const Edge&); + void updateMessage (const Edge&); + void updateValues (const Edge&); + double getResidual (const Edge&) const; + void updateResidual (const Edge&); + void clearResidual (const Edge&); + BpNode* M (const BayesNode*) const; + friend bool compareResidual (const Edge&, const Edge&); + + const BayesNet* bn_; + vector msgs_; + Schedule schedule_; + int nIter_; + int maxIter_; + double accuracy_; + vector updateOrder_; + bool forceGenericSolver_; + + struct compare + { + inline bool operator() (const Edge& e1, const Edge& e2) + { + return compareResidual (e1, e2); + } + }; + + typedef multiset SortedOrder; + SortedOrder sortedOrder_; + + typedef unordered_map EdgeMap; + EdgeMap edgeMap_; + +}; + + + +inline void +BPSolver::updatePiMessage (BayesNode* source, BayesNode* destination) +{ + M(source)->updatePiMessage(destination); +} + + + +inline void +BPSolver::updateLambdaMessage (BayesNode* source, BayesNode* destination) +{ + M(destination)->updateLambdaMessage(source); +} + + + +inline void +BPSolver::calculateNextMessage (const Edge& e) +{ + if (DL >= 1) { + cout << "calculating " << e.toString() << endl; + } + if (e.type == PI_MSG) { + calculateNextPiMessage (e.source, e.destination); + } else { + calculateNextLambdaMessage (e.source, e.destination); + } +} + + + +inline void +BPSolver::updateMessage (const Edge& e) +{ + if (DL >= 1) { + cout << "updating " << e.toString() << endl; + } + if (e.type == PI_MSG) { + M(e.source)->updatePiMessage(e.destination); + } else { + M(e.destination)->updateLambdaMessage(e.source); + } +} + + + +inline void +BPSolver::updateValues (const Edge& e) +{ + if (!e.destination->hasEvidence()) { + if (e.type == PI_MSG) { + updatePiValues (e.destination); + } else { + updateLambdaValues (e.destination); + } + } +} + + + +inline double +BPSolver::getResidual (const Edge& e) const +{ + if (e.type == PI_MSG) { + return M(e.source)->getPiResidual(e.destination); + } else { + return M(e.destination)->getLambdaResidual(e.source); + } +} + + + +inline void +BPSolver::updateResidual (const Edge& e) +{ + if (e.type == PI_MSG) { + M(e.source)->updatePiResidual(e.destination); + } else { + M(e.destination)->updateLambdaResidual(e.source); + } +} + + + +inline void +BPSolver::clearResidual (const Edge& e) +{ + if (e.type == PI_MSG) { + M(e.source)->clearPiResidual(e.destination); + } else { + M(e.destination)->clearLambdaResidual(e.source); + } +} + + + +inline bool +compareResidual (const Edge& e1, const Edge& e2) +{ + double residual1; + double residual2; + if (e1.type == PI_MSG) { + residual1 = Edge::klass->M(e1.source)->getPiResidual(e1.destination); + } else { + residual1 = Edge::klass->M(e1.destination)->getLambdaResidual(e1.source); + } + if (e2.type == PI_MSG) { + residual2 = Edge::klass->M(e2.source)->getPiResidual(e2.destination); + } else { + residual2 = Edge::klass->M(e2.destination)->getLambdaResidual(e2.source); + } + return residual1 > residual2; +} + + + +inline BpNode* +BPSolver::M (const BayesNode* node) const +{ + assert (node); + assert (node == bn_->getNode (node->getVarId())); + assert (node->getIndex() < msgs_.size()); + return msgs_[node->getIndex()]; +} + + +#endif + diff --git a/packages/CLPBN/clpbn/bp/BayesNet.cpp b/packages/CLPBN/clpbn/bp/BayesNet.cpp new file mode 100755 index 000000000..291c0c8d3 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/BayesNet.cpp @@ -0,0 +1,792 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "xmlParser/xmlParser.h" + +#include "BayesNet.h" + + +BayesNet::BayesNet (void) +{ +} + + + +BayesNet::BayesNet (const char* fileName) +{ + map domains; + XMLNode xMainNode = XMLNode::openFileHelper (fileName, "BIF"); + // only the first network is parsed, others are ignored + XMLNode xNode = xMainNode.getChildNode ("NETWORK"); + int nVars = xNode.nChildNode ("VARIABLE"); + for (int i = 0; i < nVars; i++) { + XMLNode var = xNode.getChildNode ("VARIABLE", i); + string type = var.getAttribute ("TYPE"); + if (type != "nature") { + cerr << "error: only \"nature\" variables are supported" << endl; + abort(); + } + Domain domain; + string label = var.getChildNode("NAME").getText(); + int domainSize = var.nChildNode ("OUTCOME"); + for (int j = 0; j < domainSize; j++) { + if (var.getChildNode("OUTCOME", j).getText() == 0) { + stringstream ss; + ss << j + 1; + domain.push_back (ss.str()); + } else { + domain.push_back (var.getChildNode("OUTCOME", j).getText()); + } + } + domains.insert (make_pair (label, domain)); + } + + int nDefs = xNode.nChildNode ("DEFINITION"); + if (nVars != nDefs) { + cerr << "error: different number of variables and definitions"; + cerr << endl; + } + + queue indexes; + for (int i = 0; i < nDefs; i++) { + indexes.push (i); + } + + while (!indexes.empty()) { + int index = indexes.front(); + indexes.pop(); + XMLNode def = xNode.getChildNode ("DEFINITION", index); + string label = def.getChildNode("FOR").getText(); + map::const_iterator iter; + iter = domains.find (label); + if (iter == domains.end()) { + cerr << "error: unknow variable `" << label << "'" << endl; + abort(); + } + bool processItLatter = false; + NodeSet parents; + int nParams = iter->second.size(); + for (int j = 0; j < def.nChildNode ("GIVEN"); j++) { + string parentLabel = def.getChildNode("GIVEN", j).getText(); + BayesNode* parentNode = getNode (parentLabel); + if (parentNode) { + nParams *= parentNode->getDomainSize(); + parents.push_back (parentNode); + } + else { + iter = domains.find (parentLabel); + if (iter == domains.end()) { + cerr << "error: unknow parent `" << parentLabel << "'" << endl; + abort(); + } else { + // this definition contains a parent that doesn't + // have a corresponding bayesian node instance yet, + // so process this definition latter + indexes.push (index); + processItLatter = true; + break; + } + } + } + + if (!processItLatter) { + int count = 0; + ParamSet params (nParams); + stringstream s (def.getChildNode("TABLE").getText()); + while (!s.eof() && count < nParams) { + s >> params[count]; + count ++; + } + if (count != nParams) { + cerr << "error: invalid number of parameters " ; + cerr << "for variable `" << label << "'" << endl; + abort(); + } + params = reorderParameters (params, iter->second.size()); + addNode (label, iter->second, parents, params); + } + } + setIndexes(); +} + + + +BayesNet::~BayesNet (void) +{ + Statistics::writeStats(); + for (unsigned i = 0; i < nodes_.size(); i++) { + delete nodes_[i]; + } +} + + + +BayesNode* +BayesNet::addNode (unsigned varId) +{ + indexMap_.insert (make_pair (varId, nodes_.size())); + nodes_.push_back (new BayesNode (varId)); + return nodes_.back(); +} + + + +BayesNode* +BayesNet::addNode (unsigned varId, + unsigned dsize, + int evidence, + NodeSet& parents, + Distribution* dist) +{ + indexMap_.insert (make_pair (varId, nodes_.size())); + nodes_.push_back (new BayesNode ( + varId, dsize, evidence, parents, dist)); + return nodes_.back(); +} + + + +BayesNode* +BayesNet::addNode (string label, + Domain domain, + NodeSet& parents, + ParamSet& params) +{ + indexMap_.insert (make_pair (nodes_.size(), nodes_.size())); + Distribution* dist = new Distribution (params); + BayesNode* node = new BayesNode ( + nodes_.size(), label, domain, parents, dist); + dists_.push_back (dist); + nodes_.push_back (node); + return node; +} + + + +BayesNode* +BayesNet::getNode (unsigned varId) const +{ + IndexMap::const_iterator it = indexMap_.find(varId); + if (it == indexMap_.end()) { + return 0; + } else { + return nodes_[it->second]; + } +} + + + +BayesNode* +BayesNet::getNode (string label) const +{ + BayesNode* node = 0; + for (unsigned i = 0; i < nodes_.size(); i++) { + if (nodes_[i]->getLabel() == label) { + node = nodes_[i]; + break; + } + } + return node; +} + + + +void +BayesNet::addDistribution (Distribution* dist) +{ + dists_.push_back (dist); +} + + + +Distribution* +BayesNet::getDistribution (unsigned distId) const +{ + Distribution* dist = 0; + for (unsigned i = 0; i < dists_.size(); i++) { + if (dists_[i]->id == distId) { + dist = dists_[i]; + break; + } + } + return dist; +} + + + +const NodeSet& +BayesNet::getNodes (void) const +{ + return nodes_; +} + + + +int +BayesNet::getNumberOfNodes (void) const +{ + return nodes_.size(); +} + + + +NodeSet +BayesNet::getRootNodes (void) const +{ + NodeSet roots; + for (unsigned i = 0; i < nodes_.size(); i++) { + if (nodes_[i]->isRoot()) { + roots.push_back (nodes_[i]); + } + } + return roots; +} + + + +NodeSet +BayesNet::getLeafNodes (void) const +{ + NodeSet leafs; + for (unsigned i = 0; i < nodes_.size(); i++) { + if (nodes_[i]->isLeaf()) { + leafs.push_back (nodes_[i]); + } + } + return leafs; +} + + + +VarSet +BayesNet::getVariables (void) const +{ + VarSet vars; + for (unsigned i = 0; i < nodes_.size(); i++) { + vars.push_back (nodes_[i]); + } + return vars; +} + + + +BayesNet* +BayesNet::pruneNetwork (BayesNode* queryNode) const +{ + NodeSet queryNodes; + queryNodes.push_back (queryNode); + return pruneNetwork (queryNodes); +} + + + +BayesNet* +BayesNet::pruneNetwork (const NodeSet& interestedVars) const +{ + /* + cout << "interested vars: " ; + for (unsigned i = 0; i < interestedVars.size(); i++) { + cout << interestedVars[i]->getLabel() << " " ; + } + cout << endl; + */ + vector states (nodes_.size(), 0); + + Scheduling scheduling; + for (NodeSet::const_iterator it = interestedVars.begin(); + it != interestedVars.end(); it++) { + scheduling.push (ScheduleInfo (*it, false, true)); + } + + while (!scheduling.empty()) { + ScheduleInfo& sch = scheduling.front(); + StateInfo* state = states[sch.node->getIndex()]; + if (!state) { + state = new StateInfo(); + states[sch.node->getIndex()] = state; + } else { + state->visited = true; + } + if (!sch.node->hasEvidence() && sch.visitedFromChild) { + if (!state->markedOnTop) { + state->markedOnTop = true; + scheduleParents (sch.node, scheduling); + } + if (!state->markedOnBottom) { + state->markedOnBottom = true; + scheduleChilds (sch.node, scheduling); + } + } + if (sch.visitedFromParent) { + if (sch.node->hasEvidence() && !state->markedOnTop) { + state->markedOnTop = true; + scheduleParents (sch.node, scheduling); + } + if (!sch.node->hasEvidence() && !state->markedOnBottom) { + state->markedOnBottom = true; + scheduleChilds (sch.node, scheduling); + } + } + scheduling.pop(); + } + /* + cout << "\t\ttop\tbottom" << endl; + cout << "variable\t\tmarked\tmarked\tvisited\tobserved" << endl; + cout << "----------------------------------------------------------" ; + cout << endl; + for (unsigned i = 0; i < states.size(); i++) { + cout << nodes_[i]->getLabel() << ":\t\t" ; + if (states[i]) { + states[i]->markedOnTop ? cout << "yes\t" : cout << "no\t" ; + states[i]->markedOnBottom ? cout << "yes\t" : cout << "no\t" ; + states[i]->visited ? cout << "yes\t" : cout << "no\t" ; + nodes_[i]->hasEvidence() ? cout << "yes" : cout << "no" ; + cout << endl; + } else { + cout << "no\tno\tno\t" ; + nodes_[i]->hasEvidence() ? cout << "yes" : cout << "no" ; + cout << endl; + } + } + cout << endl; + */ + BayesNet* bn = new BayesNet(); + constructGraph (bn, states); + + for (unsigned i = 0; i < nodes_.size(); i++) { + delete states[i]; + } + return bn; +} + + + +void +BayesNet::constructGraph (BayesNet* bn, + const vector& states) const +{ + for (unsigned i = 0; i < nodes_.size(); i++) { + bool isRequired = false; + if (states[i]) { + isRequired = (nodes_[i]->hasEvidence() && states[i]->visited) + || + states[i]->markedOnTop; + } + if (isRequired) { + NodeSet parents; + if (states[i]->markedOnTop) { + const NodeSet& ps = nodes_[i]->getParents(); + for (unsigned j = 0; j < ps.size(); j++) { + BayesNode* parent = bn->getNode (ps[j]->getVarId()); + if (!parent) { + parent = bn->addNode (ps[j]->getVarId()); + } + parents.push_back (parent); + } + } + BayesNode* node = bn->getNode (nodes_[i]->getVarId()); + if (node) { + node->setData (nodes_[i]->getDomainSize(), + nodes_[i]->getEvidence(), parents, + nodes_[i]->getDistribution()); + } else { + node = bn->addNode (nodes_[i]->getVarId(), + nodes_[i]->getDomainSize(), + nodes_[i]->getEvidence(), parents, + nodes_[i]->getDistribution()); + } + if (nodes_[i]->hasDomain()) { + node->setDomain (nodes_[i]->getDomain()); + } + if (nodes_[i]->hasLabel()) { + node->setLabel (nodes_[i]->getLabel()); + } + } + } + bn->setIndexes(); +} + +/* +void +BayesNet::constructGraph (BayesNet* bn, + const vector& states) const +{ + for (unsigned i = 0; i < nodes_.size(); i++) { + if (states[i]) { + if (nodes_[i]->hasEvidence() && states[i]->visited) { + NodeSet parents; + if (states[i]->markedOnTop) { + const NodeSet& ps = nodes_[i]->getParents(); + for (unsigned j = 0; j < ps.size(); j++) { + BayesNode* parent = bn->getNode (ps[j]->getVarId()); + if (parent == 0) { + parent = bn->addNode (ps[j]->getVarId()); + } + parents.push_back (parent); + } + } + + BayesNode* n = bn->getNode (nodes_[i]->getVarId()); + if (n) { + n->setData (nodes_[i]->getDomainSize(), + nodes_[i]->getEvidence(), parents, + nodes_[i]->getDistribution()); + } else { + bn->addNode (nodes_[i]->getVarId(), + nodes_[i]->getDomainSize(), + nodes_[i]->getEvidence(), parents, + nodes_[i]->getDistribution()); + } + + } else if (states[i]->markedOnTop) { + NodeSet parents; + const NodeSet& ps = nodes_[i]->getParents(); + for (unsigned j = 0; j < ps.size(); j++) { + BayesNode* parent = bn->getNode (ps[j]->getVarId()); + if (parent == 0) { + parent = bn->addNode (ps[j]->getVarId()); + } + parents.push_back (parent); + } + + BayesNode* n = bn->getNode (nodes_[i]->getVarId()); + if (n) { + n->setData (nodes_[i]->getDomainSize(), + nodes_[i]->getEvidence(), parents, + nodes_[i]->getDistribution()); + } else { + bn->addNode (nodes_[i]->getVarId(), + nodes_[i]->getDomainSize(), + nodes_[i]->getEvidence(), parents, + nodes_[i]->getDistribution()); + } + } + } + } +}*/ + + + +bool +BayesNet::isSingleConnected (void) const +{ + return !containsUndirectedCycle(); +} + + + +vector +BayesNet::getDomainConfigurationsOf (const NodeSet& nodes) +{ + int nConfs = 1; + for (unsigned i = 0; i < nodes.size(); i++) { + nConfs *= nodes[i]->getDomainSize(); + } + + vector confs (nConfs); + for (int i = 0; i < nConfs; i++) { + confs[i].resize (nodes.size()); + } + + int nReps = 1; + for (int i = nodes.size() - 1; i >= 0; i--) { + int index = 0; + while (index < nConfs) { + for (int j = 0; j < nodes[i]->getDomainSize(); j++) { + for (int r = 0; r < nReps; r++) { + confs[index][i] = j; + index++; + } + } + } + nReps *= nodes[i]->getDomainSize(); + } + + return confs; +} + + + +vector +BayesNet::getInstantiations (const NodeSet& parents_) +{ + int nParents = parents_.size(); + int rowSize = 1; + for (unsigned i = 0; i < parents_.size(); i++) { + rowSize *= parents_[i]->getDomainSize(); + } + int nReps = 1; + vector headers (rowSize); + for (int i = nParents - 1; i >= 0; i--) { + Domain domain = parents_[i]->getDomain(); + int index = 0; + while (index < rowSize) { + for (int j = 0; j < parents_[i]->getDomainSize(); j++) { + for (int r = 0; r < nReps; r++) { + if (headers[index] != "") { + headers[index] = domain[j] + "," + headers[index]; + } else { + headers[index] = domain[j]; + } + index++; + } + } + } + nReps *= parents_[i]->getDomainSize(); + } + return headers; +} + + + +void +BayesNet::setIndexes (void) +{ + for (unsigned i = 0; i < nodes_.size(); i++) { + nodes_[i]->setIndex (i); + } +} + + + +void +BayesNet::freeDistributions (void) +{ + for (unsigned i = 0; i < dists_.size(); i++) { + delete dists_[i]; + } +} + + + +void +BayesNet::printNetwork (void) const +{ + for (unsigned i = 0; i < nodes_.size(); i++) { + cout << *nodes_[i]; + } +} + + + +void +BayesNet::printNetworkToFile (const char* fileName) const +{ + string s = "../../" ; + s += fileName; + ofstream out (s.c_str()); + if (!out.is_open()) { + cerr << "error: cannot open file to write at " ; + cerr << "BayesNet::printToFile()" << endl; + abort(); + } + for (unsigned i = 0; i < nodes_.size(); i++) { + out << *nodes_[i]; + } + out.close(); +} + + + +void +BayesNet::exportToDotFile (const char* fileName, + bool showNeighborless, + const NodeSet& highlightNodes) const +{ + string s = "../../" ; + s+= fileName; + ofstream out (s.c_str()); + if (!out.is_open()) { + cerr << "error: cannot open file to write at " ; + cerr << "BayesNet::exportToDotFile()" << endl; + abort(); + } + + out << "digraph \"" << fileName << "\" {" << endl; + for (unsigned i = 0; i < nodes_.size(); i++) { + const NodeSet& childs = nodes_[i]->getChilds(); + for (unsigned j = 0; j < childs.size(); j++) { + out << '"' << nodes_[i]->getLabel() << '"' << " -> " ; + out << '"' << childs[j]->getLabel() << '"' << endl; + } + } + + for (unsigned i = 0; i < nodes_.size(); i++) { + if (showNeighborless || nodes_[i]->hasNeighbors()) { + out << '"' << nodes_[i]->getLabel() << '"' ; + if (nodes_[i]->hasEvidence()) { + out << " [style=filled, fillcolor=yellow]" << endl; + } else { + out << endl; + } + } + } + + for (unsigned i = 0; i < highlightNodes.size(); i++) { + out << '"' << highlightNodes[i]->getLabel() << '"' ; + out << " [shape=box]" << endl; + } + + out << "}" << endl; + out.close(); +} + + + +void +BayesNet::exportToBifFile (const char* fileName) const +{ + string s = "../../" ; + s += fileName; + ofstream out (s.c_str()); + if(!out.is_open()) { + cerr << "error: cannot open file to write at " ; + cerr << "BayesNet::exportToBifFile()" << endl; + abort(); + } + out << "" << endl; + out << "" << endl; + out << "" << endl; + out << "" << fileName << "" << endl << endl; + for (unsigned i = 0; i < nodes_.size(); i++) { + out << "" << endl; + out << "\t" << nodes_[i]->getLabel() << "" << endl; + const Domain& domain = nodes_[i]->getDomain(); + for (unsigned j = 0; j < domain.size(); j++) { + out << "\t" << domain[j] << "" << endl; + } + out << "" << endl << endl; + } + + for (unsigned i = 0; i < nodes_.size(); i++) { + out << "" << endl; + out << "\t" << nodes_[i]->getLabel() << "" << endl; + const NodeSet& parents = nodes_[i]->getParents(); + for (unsigned j = 0; j < parents.size(); j++) { + out << "\t" << parents[j]->getLabel(); + out << "" << endl; + } + ParamSet params = revertParameterReorder (nodes_[i]->getParameters(), + nodes_[i]->getDomainSize()); + out << "\t" ; + for (unsigned j = 0; j < params.size(); j++) { + out << " " << params[j]; + } + out << "
" << endl; + out << "
" << endl << endl; + } + out << "
" << endl; + out << "
" << endl << endl; + out.close(); +} + + + +bool +BayesNet::containsUndirectedCycle (void) const +{ + vector visited (nodes_.size(), false); + for (unsigned i = 0; i < nodes_.size(); i++) { + int v = nodes_[i]->getIndex(); + if (!visited[v]) { + if (containsUndirectedCycle (v, -1, visited)) { + return true; + } + } + } + return false; +} + + + +bool +BayesNet::containsUndirectedCycle (int v, + int p, + vector& visited) const +{ + visited[v] = true; + vector adjacencies = getAdjacentNodes (v); + for (unsigned i = 0; i < adjacencies.size(); i++) { + int w = adjacencies[i]; + if (!visited[w]) { + if (containsUndirectedCycle (w, v, visited)) { + return true; + } + } + else if (visited[w] && w != p) { + return true; + } + } + return false; // no cycle detected in this component +} + + + +vector +BayesNet::getAdjacentNodes (int v) const +{ + vector adjacencies; + const NodeSet& parents = nodes_[v]->getParents(); + const NodeSet& childs = nodes_[v]->getChilds(); + for (unsigned i = 0; i < parents.size(); i++) { + adjacencies.push_back (parents[i]->getIndex()); + } + for (unsigned i = 0; i < childs.size(); i++) { + adjacencies.push_back (childs[i]->getIndex()); + } + return adjacencies; +} + + + +ParamSet +BayesNet::reorderParameters (const ParamSet& params, + int domainSize) const +{ + // the interchange format for bayesian networks keeps the probabilities + // in the following order: + // p(a1|b1,c1) p(a2|b1,c1) p(a1|b1,c2) p(a2|b1,c2) p(a1|b2,c1) p(a2|b2,c1) + // p(a1|b2,c2) p(a2|b2,c2). + // + // however, in clpbn we keep the probabilities in this order: + // p(a1|b1,c1) p(a1|b1,c2) p(a1|b2,c1) p(a1|b2,c2) p(a2|b1,c1) p(a2|b1,c2) + // p(a2|b2,c1) p(a2|b2,c2). + unsigned count = 0; + unsigned rowSize = params.size() / domainSize; + ParamSet reordered; + while (reordered.size() < params.size()) { + unsigned idx = count; + for (unsigned i = 0; i < rowSize; i++) { + reordered.push_back (params[idx]); + idx += domainSize; + } + count++; + } + return reordered; +} + + + +ParamSet +BayesNet::revertParameterReorder (const ParamSet& params, + int domainSize) const +{ + unsigned count = 0; + unsigned rowSize = params.size() / domainSize; + ParamSet reordered; + while (reordered.size() < params.size()) { + unsigned idx = count; + for (int i = 0; i < domainSize; i++) { + reordered.push_back (params[idx]); + idx += rowSize; + } + count ++; + } + return reordered; +} + diff --git a/packages/CLPBN/clpbn/bp/BayesNet.h b/packages/CLPBN/clpbn/bp/BayesNet.h new file mode 100755 index 000000000..547671f31 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/BayesNet.h @@ -0,0 +1,129 @@ +#ifndef BP_BAYES_NET_H +#define BP_BAYES_NET_H + +#include +#include +#include +#include +#include +#include + +#include "GraphicalModel.h" +#include "BayesNode.h" +#include "Shared.h" + + +using namespace std; + +class Distribution; + +struct ScheduleInfo +{ + ScheduleInfo (BayesNode* n, bool vfp, bool vfc) + { + node = n; + visitedFromParent = vfp; + visitedFromChild = vfc; + } + BayesNode* node; + bool visitedFromParent; + bool visitedFromChild; +}; + + +struct StateInfo +{ + StateInfo (void) + { + visited = true; + markedOnTop = false; + markedOnBottom = false; + } + bool visited; + bool markedOnTop; + bool markedOnBottom; +}; + +typedef vector DistSet; +typedef queue > Scheduling; +typedef unordered_map Histogram; +typedef unordered_map Times; + + +class BayesNet : public GraphicalModel +{ + public: + BayesNet (void); + BayesNet (const char*); + ~BayesNet (void); + + BayesNode* addNode (unsigned); + BayesNode* addNode (unsigned, unsigned, int, NodeSet&, Distribution*); + BayesNode* addNode (string, Domain, NodeSet&, ParamSet&); + BayesNode* getNode (unsigned) const; + BayesNode* getNode (string) const; + void addDistribution (Distribution*); + Distribution* getDistribution (unsigned) const; + const NodeSet& getNodes (void) const; + int getNumberOfNodes (void) const; + NodeSet getRootNodes (void) const; + NodeSet getLeafNodes (void) const; + VarSet getVariables (void) const; + BayesNet* pruneNetwork (BayesNode*) const; + BayesNet* pruneNetwork (const NodeSet& queryNodes) const; + void constructGraph (BayesNet*, const vector&) const; + bool isSingleConnected (void) const; + static vector getDomainConfigurationsOf (const NodeSet&); + static vector getInstantiations (const NodeSet& nodes); + void setIndexes (void); + void freeDistributions (void); + void printNetwork (void) const; + void printNetworkToFile (const char*) const; + void exportToDotFile (const char*, bool = true, + const NodeSet& = NodeSet()) const; + void exportToBifFile (const char*) const; + + static Histogram histogram_; + static Times times_; + + private: + DISALLOW_COPY_AND_ASSIGN (BayesNet); + + bool containsUndirectedCycle (void) const; + bool containsUndirectedCycle (int, int, + vector&)const; + vector getAdjacentNodes (int) const ; + ParamSet reorderParameters (const ParamSet&, int) const; + ParamSet revertParameterReorder (const ParamSet&, int) const; + void scheduleParents (const BayesNode*, Scheduling&) const; + void scheduleChilds (const BayesNode*, Scheduling&) const; + + NodeSet nodes_; + DistSet dists_; + IndexMap indexMap_; +}; + + + +inline void +BayesNet::scheduleParents (const BayesNode* n, Scheduling& sch) const +{ + const NodeSet& ps = n->getParents(); + for (NodeSet::const_iterator it = ps.begin(); it != ps.end(); it++) { + sch.push (ScheduleInfo (*it, false, true)); + } +} + + + +inline void +BayesNet::scheduleChilds (const BayesNode* n, Scheduling& sch) const +{ + const NodeSet& cs = n->getChilds(); + for (NodeSet::const_iterator it = cs.begin(); it != cs.end(); it++) { + sch.push (ScheduleInfo (*it, true, false)); + } +} + +#endif + diff --git a/packages/CLPBN/clpbn/bp/BayesNode.cpp b/packages/CLPBN/clpbn/bp/BayesNode.cpp new file mode 100755 index 000000000..72ed5febd --- /dev/null +++ b/packages/CLPBN/clpbn/bp/BayesNode.cpp @@ -0,0 +1,355 @@ +#include +#include +#include +#include +#include + +#include "BayesNode.h" + + +BayesNode::BayesNode (unsigned varId) : Variable (varId) +{ +} + + + +BayesNode::BayesNode (unsigned varId, + unsigned dsize, + int evidence, + const NodeSet& parents, + Distribution* dist) : Variable(varId, dsize, evidence) +{ + parents_ = parents; + dist_ = dist; + for (unsigned int i = 0; i < parents.size(); i++) { + parents[i]->addChild (this); + } +} + + + +BayesNode::BayesNode (unsigned varId, + string label, + const Domain& domain, + const NodeSet& parents, + Distribution* dist) : Variable(varId, domain) +{ + label_ = new string (label); + parents_ = parents; + dist_ = dist; + for (unsigned int i = 0; i < parents.size(); i++) { + parents[i]->addChild (this); + } +} + + + +void +BayesNode::setData (unsigned dsize, + int evidence, + const NodeSet& parents, + Distribution* dist) +{ + setDomainSize (dsize); + evidence_ = evidence; + parents_ = parents; + dist_ = dist; + for (unsigned int i = 0; i < parents.size(); i++) { + parents[i]->addChild (this); + } +} + + + +void +BayesNode::addChild (BayesNode* node) +{ + childs_.push_back (node); +} + + + +Distribution* +BayesNode::getDistribution (void) +{ + return dist_; +} + + + +const ParamSet& +BayesNode::getParameters (void) +{ + return dist_->params; +} + + + +ParamSet +BayesNode::getRow (int rowIndex) const +{ + int rowSize = getRowSize(); + int offset = rowSize * rowIndex; + ParamSet row (rowSize); + for (int i = 0; i < rowSize; i++) { + row[i] = dist_->params[offset + i] ; + } + return row; +} + + + +bool +BayesNode::isRoot (void) +{ + return getParents().empty(); +} + + + +bool +BayesNode::isLeaf (void) +{ + return getChilds().empty(); +} + + + +bool +BayesNode::hasNeighbors (void) const +{ + return childs_.size() != 0 || parents_.size() != 0; +} + + +int +BayesNode::getCptSize (void) +{ + return dist_->params.size(); +} + + + +const vector& +BayesNode::getCptEntries (void) +{ + if (dist_->entries.size() == 0) { + unsigned rowSize = getRowSize(); + unsigned nParents = parents_.size(); + vector confs (rowSize); + + for (unsigned i = 0; i < rowSize; i++) { + confs[i].resize (nParents); + } + + int nReps = 1; + for (int i = nParents - 1; i >= 0; i--) { + unsigned index = 0; + while (index < rowSize) { + for (int j = 0; j < parents_[i]->getDomainSize(); j++) { + for (int r = 0; r < nReps; r++) { + confs[index][i] = j; + index++; + } + } + } + nReps *= parents_[i]->getDomainSize(); + } + + dist_->entries.reserve (rowSize); + for (unsigned i = 0; i < rowSize; i++) { + dist_->entries.push_back (CptEntry (i, confs[i])); + } + } + return dist_->entries; +} + + + +int +BayesNode::getIndexOfParent (const BayesNode* parent) const +{ + for (unsigned int i = 0; i < parents_.size(); i++) { + if (parents_[i] == parent) { + return i; + } + } + return -1; +} + + + +string +BayesNode::cptEntryToString (const CptEntry& entry) const +{ + stringstream ss; + ss << "p(" ; + const DomainConf& conf = entry.getParentConfigurations(); + int row = entry.getParameterIndex() / getRowSize(); + ss << getDomain()[row]; + if (parents_.size() > 0) { + ss << "|" ; + for (unsigned int i = 0; i < conf.size(); i++) { + if (i != 0) { + ss << ","; + } + ss << parents_[i]->getDomain()[conf[i]]; + } + } + ss << ")" ; + return ss.str(); +} + + + +string +BayesNode::cptEntryToString (int row, const CptEntry& entry) const +{ + stringstream ss; + ss << "p(" ; + const DomainConf& conf = entry.getParentConfigurations(); + ss << getDomain()[row]; + if (parents_.size() > 0) { + ss << "|" ; + for (unsigned int i = 0; i < conf.size(); i++) { + if (i != 0) { + ss << ","; + } + ss << parents_[i]->getDomain()[conf[i]]; + } + } + ss << ")" ; + return ss.str(); +} + + + +vector +BayesNode::getDomainHeaders (void) const +{ + int nParents = parents_.size(); + int rowSize = getRowSize(); + int nReps = 1; + vector headers (rowSize); + for (int i = nParents - 1; i >= 0; i--) { + Domain domain = parents_[i]->getDomain(); + int index = 0; + while (index < rowSize) { + for (int j = 0; j < parents_[i]->getDomainSize(); j++) { + for (int r = 0; r < nReps; r++) { + if (headers[index] != "") { + headers[index] = domain[j] + "," + headers[index]; + } else { + headers[index] = domain[j]; + } + index++; + } + } + } + nReps *= parents_[i]->getDomainSize(); + } + return headers; +} + + + +ostream& +operator << (ostream& o, const BayesNode& node) +{ + o << "variable " << node.getIndex() << endl; + o << "Var Id: " << node.getVarId() << endl; + o << "Label: " << node.getLabel() << endl; + + o << "Evidence: " ; + if (node.hasEvidence()) { + o << node.getEvidence(); + } + else { + o << "no" ; + } + o << endl; + + o << "Parents: " ; + const NodeSet& parents = node.getParents(); + if (parents.size() != 0) { + for (unsigned int i = 0; i < parents.size() - 1; i++) { + o << parents[i]->getLabel() << ", " ; + } + o << parents[parents.size() - 1]->getLabel(); + } + o << endl; + + o << "Childs: " ; + const NodeSet& childs = node.getChilds(); + if (childs.size() != 0) { + for (unsigned int i = 0; i < childs.size() - 1; i++) { + o << childs[i]->getLabel() << ", " ; + } + o << childs[childs.size() - 1]->getLabel(); + } + o << endl; + + o << "Domain: " ; + Domain domain = node.getDomain(); + for (unsigned int i = 0; i < domain.size() - 1; i++) { + o << domain[i] << ", " ; + } + if (domain.size() != 0) { + o << domain[domain.size() - 1]; + } + o << endl; + + // min width of first column + const unsigned int MIN_DOMAIN_WIDTH = 4; + // min width of following columns + const unsigned int MIN_COMBO_WIDTH = 12; + + unsigned int domainWidth = domain[0].length(); + for (unsigned int i = 1; i < domain.size(); i++) { + if (domain[i].length() > domainWidth) { + domainWidth = domain[i].length(); + } + } + domainWidth = (domainWidth < MIN_DOMAIN_WIDTH) + ? MIN_DOMAIN_WIDTH + : domainWidth; + + o << left << setw (domainWidth) << "cpt" << right; + + vector widths; + int lineWidth = domainWidth; + vector headers = node.getDomainHeaders(); + + if (!headers.empty()) { + for (unsigned int i = 0; i < headers.size(); i++) { + unsigned int len = headers[i].length(); + int w = (len < MIN_COMBO_WIDTH) ? MIN_COMBO_WIDTH : len; + widths.push_back (w); + o << setw (w) << headers[i]; + lineWidth += w; + } + o << endl; + } else { + cout << endl; + widths.push_back (domainWidth); + lineWidth += MIN_COMBO_WIDTH; + } + + for (int i = 0; i < lineWidth; i++) { + o << "-" ; + } + o << endl; + + for (unsigned int i = 0; i < domain.size(); i++) { + ParamSet row = node.getRow (i); + o << left << setw (domainWidth) << domain[i] << right; + for (unsigned j = 0; j < node.getRowSize(); j++) { + o << setw (widths[j]) << row[j]; + } + o << endl; + } + o << endl; + + return o; +} + diff --git a/packages/CLPBN/clpbn/bp/BayesNode.h b/packages/CLPBN/clpbn/bp/BayesNode.h new file mode 100755 index 000000000..923cca0f1 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/BayesNode.h @@ -0,0 +1,91 @@ +#ifndef BP_BAYESNODE_H +#define BP_BAYESNODE_H + +#include +#include +#include + +#include "Variable.h" +#include "CptEntry.h" +#include "Distribution.h" +#include "Shared.h" + +using namespace std; + + +class BayesNode : public Variable +{ + public: + BayesNode (unsigned); + BayesNode (unsigned, unsigned, int, const NodeSet&, Distribution*); + BayesNode (unsigned, string, const Domain&, const NodeSet&, Distribution*); + + void setData (unsigned, int, const NodeSet&, Distribution*); + void addChild (BayesNode*); + Distribution* getDistribution (void); + const ParamSet& getParameters (void); + ParamSet getRow (int) const; + void setProbability (int, const CptEntry&, double); + bool isRoot (void); + bool isLeaf (void); + bool hasNeighbors (void) const; + int getCptSize (void); + const vector& getCptEntries (void); + int getIndexOfParent (const BayesNode*) const; + string cptEntryToString (const CptEntry&) const; + string cptEntryToString (int, const CptEntry&) const; + // inlines + const NodeSet& getParents (void) const; + const NodeSet& getChilds (void) const; + double getProbability (int, const CptEntry& entry); + unsigned getRowSize (void) const; + + private: + DISALLOW_COPY_AND_ASSIGN (BayesNode); + + Domain getDomainHeaders (void) const; + friend ostream& operator << (ostream&, const BayesNode&); + + NodeSet parents_; + NodeSet childs_; + Distribution* dist_; +}; + +ostream& operator << (ostream&, const BayesNode&); + + + +inline const NodeSet& +BayesNode::getParents (void) const +{ + return parents_; +} + + + +inline const NodeSet& +BayesNode::getChilds (void) const +{ + return childs_; +} + + + +inline double +BayesNode::getProbability (int row, const CptEntry& entry) +{ + int col = entry.getParameterIndex(); + int idx = (row * getRowSize()) + col; + return dist_->params[idx]; +} + + + +inline unsigned +BayesNode::getRowSize (void) const +{ + return dist_->params.size() / getDomainSize(); +} + +#endif + diff --git a/packages/CLPBN/clpbn/bp/BpNode.cpp b/packages/CLPBN/clpbn/bp/BpNode.cpp index 8f784ab16..4fd52f95c 100644 --- a/packages/CLPBN/clpbn/bp/BpNode.cpp +++ b/packages/CLPBN/clpbn/bp/BpNode.cpp @@ -4,226 +4,44 @@ #include "BpNode.h" +bool BpNode::calculateMessageResidual_ = true; -bool BpNode::parallelSchedule_ = false; -BpNode::BpNode (string varName, - vector parents, - Distribution* dist, - int evidence) : BayesianNode (varName, parents, dist, evidence) +BpNode::BpNode (BayesNode* node) { - -} - - - -BpNode::~BpNode (void) -{ - delete [] piValues_; - delete [] lambdaValues_; - delete [] oldBeliefs_; - map::iterator iter; - for (iter = lambdaMessages_.begin(); iter != lambdaMessages_.end(); ++iter) { - delete [] iter->second; + ds_ = node->getDomainSize(); + const NodeSet& childs = node->getChilds(); + piVals_.resize (ds_, 1); + ldVals_.resize (ds_, 1); + if (calculateMessageResidual_) { + piResiduals_.resize (childs.size(), 0.0); + ldResiduals_.resize (childs.size(), 0.0); } - for (iter = piMessages_.begin(); iter != piMessages_.end(); ++iter) { - delete [] iter->second; - } - // FIXME delete new messages -} - - - -void -BpNode::enableParallelSchedule (void) -{ - parallelSchedule_ = true; -} - - - -void -BpNode::allocateMemory (void) -{ - // FIXME do i need this !? - int domainSize = getDomainSize(); - piValues_ = new double [domainSize]; - lambdaValues_ = new double [domainSize]; - if (parallelSchedule_) { - newPiMessages_ = new map; - newLambdaMessages_ = new map; - } - oldBeliefs_ = 0; - vector childs = getChilds(); - for (unsigned int i = 0; i < childs.size(); i++) { - BpNode* child = static_cast (childs[i]); - piMessages_.insert (make_pair (child, new double [domainSize])); - lambdaMessages_.insert (make_pair (child, new double [domainSize])); - if (parallelSchedule_) { - newPiMessages_->insert (make_pair (child, new double [domainSize])); - newLambdaMessages_->insert (make_pair (child, new double [domainSize])); - } + childs_ = &childs; + for (unsigned i = 0; i < childs.size(); i++) { + //indexMap_.insert (make_pair (childs[i]->getVarId(), i)); + currPiMsgs_.push_back (ParamSet (ds_, 1)); + currLdMsgs_.push_back (ParamSet (ds_, 1)); + nextPiMsgs_.push_back (ParamSet (ds_, 1)); + nextLdMsgs_.push_back (ParamSet (ds_, 1)); } } -double* -BpNode::getPiValues (void) const +ParamSet +BpNode::getBeliefs (void) const { - return piValues_; -} - - - -double -BpNode::getPiValue (int index) const -{ - const int c = getDomainSize(); - assert (index >=0 && index < c); - return piValues_[index]; -} - - - -void -BpNode::setPiValue (int index, double value) -{ - const int c = getDomainSize(); - assert (index >=0 && index < c); - piValues_[index] = value; -} - - - -double* -BpNode::getLambdaValues (void) const -{ - return lambdaValues_; -} - - - -double -BpNode::getLambdaValue (int index) const -{ - const int c = getDomainSize(); - assert (index >=0 && index < c); - return lambdaValues_[index]; -} - - - -void -BpNode::setLambdaValue (int index, double value) -{ - const int c = getDomainSize(); - assert (index >=0 && index < c); - lambdaValues_[index] = value; -} - - - -double* -BpNode::getPiMessages (BpNode* node) const -{ - assert (node); - map::const_iterator iter = piMessages_.find (node); - assert (iter != piMessages_.end()); - return iter->second; -} - - - -double -BpNode::getPiMessage (BpNode* node, int index) const -{ - assert (node); - const int c = getDomainSize(); - assert (index >=0 && index < c); - map::const_iterator iter = piMessages_.find (node); - assert (iter != piMessages_.end()); - return iter->second[index]; -} - - - -void -BpNode::setPiMessage (BpNode* node, int index, double probability) -{ - assert (node); - const int c = getDomainSize(); - assert (index >=0 && index < c); - map::const_iterator iter; - if (parallelSchedule_) { - // cerr << "set_pi_message" << endl; - iter = newPiMessages_->find (node); - assert (iter != newPiMessages_->end()); - } else { - iter = piMessages_.find (node); - assert (iter != piMessages_.end()); - } - iter->second[index] = probability; -} - - - -double* -BpNode::getLambdaMessages (BpNode* node) const -{ - assert (node); - map::const_iterator iter = lambdaMessages_.find (node); - assert (iter != piMessages_.end()); - return iter->second; -} - - - -double -BpNode::getLambdaMessage (BpNode* node, int index) const -{ - assert (node); - const int c = getDomainSize(); - assert (index >=0 && index < c); - map::const_iterator iter = lambdaMessages_.find (node); - assert (iter != piMessages_.end()); - return iter->second[index]; -} - - - -void -BpNode::setLambdaMessage (BpNode* node, int index, double probability) -{ - assert (node); - const int c = getDomainSize(); - assert (index >=0 && index < c); - map::const_iterator iter; - if (parallelSchedule_) { - //cerr << "set_lambda_message" << endl; - iter = newLambdaMessages_->find (node); - assert (iter != newLambdaMessages_->end()); - } else { - iter = lambdaMessages_.find (node); - assert (iter != lambdaMessages_.end()); - } - iter->second[index] = probability; -} - - - -double* -BpNode::getBeliefs (void) -{ - double sum = 0.0; - double* beliefs = new double [getDomainSize()]; - for (int xi = 0; xi < getDomainSize(); xi++) { - double prod = piValues_[xi] * lambdaValues_[xi]; + double sum = 0.0; + ParamSet beliefs (ds_); + for (int xi = 0; xi < ds_; xi++) { + double prod = piVals_[xi] * ldVals_[xi]; beliefs[xi] = prod; sum += prod; } - // normalize the beliefs - for (int xi = 0; xi < getDomainSize(); xi++) { + assert (sum); + //normalize the beliefs + for (int xi = 0; xi < ds_; xi++) { beliefs[xi] /= sum; } return beliefs; @@ -231,91 +49,202 @@ BpNode::getBeliefs (void) +double +BpNode::getPiValue (int idx) const +{ + assert (idx >=0 && idx < ds_); + return piVals_[idx]; +} + + + +void +BpNode::setPiValue (int idx, double value) +{ + assert (idx >=0 && idx < ds_); + piVals_[idx] = value; +} + + + +double +BpNode::getLambdaValue (int idx) const +{ + assert (idx >=0 && idx < ds_); + return ldVals_[idx]; +} + + + +void +BpNode::setLambdaValue (int idx, double value) +{ + assert (idx >=0 && idx < ds_); + ldVals_[idx] = value; +} + + + +ParamSet& +BpNode::getPiValues (void) +{ + return piVals_; +} + + + +ParamSet& +BpNode::getLambdaValues (void) +{ + return ldVals_; +} + + + +double +BpNode::getPiMessageValue (const BayesNode* destination, int idx) const +{ + assert (idx >=0 && idx < ds_); + return currPiMsgs_[getIndex(destination)][idx]; +} + + + +double +BpNode::getLambdaMessageValue (const BayesNode* source, int idx) const +{ + assert (idx >=0 && idx < ds_); + return currLdMsgs_[getIndex(source)][idx]; +} + + + +const ParamSet& +BpNode::getPiMessage (const BayesNode* destination) const +{ + return currPiMsgs_[getIndex(destination)]; +} + + + +const ParamSet& +BpNode::getLambdaMessage (const BayesNode* source) const +{ + return currLdMsgs_[getIndex(source)]; +} + + + +ParamSet& +BpNode::piNextMessageReference (const BayesNode* destination) +{ + return nextPiMsgs_[getIndex(destination)]; +} + + + +ParamSet& +BpNode::lambdaNextMessageReference (const BayesNode* source) +{ + return nextLdMsgs_[getIndex(source)]; +} + + + +void +BpNode::updatePiMessage (const BayesNode* destination) +{ + int idx = getIndex (destination); + currPiMsgs_[idx] = nextPiMsgs_[idx]; + Util::normalize (currPiMsgs_[idx]); +} + + + +void +BpNode::updateLambdaMessage (const BayesNode* source) +{ + int idx = getIndex (source); + currLdMsgs_[idx] = nextLdMsgs_[idx]; + Util::normalize (currLdMsgs_[idx]); +} + + + double BpNode::getBeliefChange (void) { double change = 0.0; - if (!oldBeliefs_) { + if (oldBeliefs_.size() == 0) { oldBeliefs_ = getBeliefs(); - change = MAX_CHANGE_; + change = 9999999999.0; } else { - double* currentBeliefs = getBeliefs(); - for (int xi = 0; xi < getDomainSize(); xi++) { + ParamSet currentBeliefs = getBeliefs(); + for (int xi = 0; xi < ds_; xi++) { change += abs (currentBeliefs[xi] - oldBeliefs_[xi]); } oldBeliefs_ = currentBeliefs; } - //FIXME memory leak return change; } void -BpNode::normalizeMessages (void) +BpNode::updatePiResidual (const BayesNode* destination) { - map::iterator iter; - - iter = lambdaMessages_.begin(); - while (iter != lambdaMessages_.end()) { - double* v = iter->second; - double sum = 0.0; - for (int xi = 0; xi < getDomainSize(); xi++) { - sum += v[xi]; - } - for (int xi = 0; xi < getDomainSize(); xi++) { - v[xi] /= sum; - } - iter ++; - } - - iter = piMessages_.begin(); - while (iter != piMessages_.end()) { - double* v = iter->second; - double sum = 0.0; - for (int xi = 0; xi < getDomainSize(); xi++) { - sum += v[xi]; - } - for (int xi = 0; xi < getDomainSize(); xi++) { - v[xi] /= sum; - } - iter ++; - } + int idx = getIndex (destination); + Util::normalize (nextPiMsgs_[idx]); + //piResiduals_[idx] = Util::getL1dist ( + // currPiMsgs_[idx], nextPiMsgs_[idx]); + piResiduals_[idx] = Util::getMaxNorm ( + currPiMsgs_[idx], nextPiMsgs_[idx]); } void -BpNode::swapMessages (void) +BpNode::updateLambdaResidual (const BayesNode* source) { - //FIXME fast way to do this - map::iterator iter1; - map::iterator iter2; - - iter1 = lambdaMessages_.begin(); - iter2 = newLambdaMessages_->begin(); - while (iter1 != lambdaMessages_.end()) { - double* v1 = iter1->second; - double* v2 = iter2->second; - for (int xi = 0; xi < getDomainSize(); xi++) { - //v1[xi] = v2[xi]; - v1[xi] = (v1[xi] + v2[xi]) / 2; - } - iter1 ++; - iter2 ++; - } - - iter1 = piMessages_.begin(); - iter2 = newPiMessages_->begin(); - while (iter1 != piMessages_.end()) { - double* v1 = iter1->second; - double* v2 = iter2->second; - for (int xi = 0; xi < getDomainSize(); xi++) { - //v1[xi] = v2[xi]; - v1[xi] = (v1[xi] + v2[xi]) / 2; - } - iter1 ++; - iter2 ++; - } + int idx = getIndex (source); + Util::normalize (nextLdMsgs_[idx]); + //ldResiduals_[idx] = Util::getL1dist ( + // currLdMsgs_[idx], nextLdMsgs_[idx]); + ldResiduals_[idx] = Util::getMaxNorm ( + currLdMsgs_[idx], nextLdMsgs_[idx]); +} + + + +void +BpNode::clearPiResidual (const BayesNode* destination) +{ + piResiduals_[getIndex(destination)] = 0; +} + + + +void +BpNode::clearLambdaResidual (const BayesNode* source) +{ + ldResiduals_[getIndex(source)] = 0; +} + + + +bool +BpNode::hasReceivedChildInfluence (void) const +{ + // if all lambda values are equal, then neither + // this node neither its descendents have evidence, + // we can use this to don't send lambda messages his parents + bool childInfluenced = false; + for (int xi = 1; xi < ds_; xi++) { + if (ldVals_[xi] != ldVals_[0]) { + childInfluenced = true; + break; + } + } + return childInfluenced; } diff --git a/packages/CLPBN/clpbn/bp/BpNode.h b/packages/CLPBN/clpbn/bp/BpNode.h index 2d796365e..2b84a298d 100644 --- a/packages/CLPBN/clpbn/bp/BpNode.h +++ b/packages/CLPBN/clpbn/bp/BpNode.h @@ -1,56 +1,99 @@ -#ifndef BP_BP_NODE_H -#define BP_BP_NODE_H +#ifndef BP_BPNODE_H +#define BP_BPNODE_H #include #include -#include #include +#include -#include "BayesianNode.h" +#include "BayesNode.h" +#include "Shared.h" using namespace std; -class BpNode : public BayesianNode +class BpNode { public: - // constructs - BpNode (string, vector, Distribution* dist, int = -1); - // destruct - ~BpNode (void); - // methods - static void enableParallelSchedule (void); - void allocateMemory (void); - double* getPiValues (void) const; - double getPiValue (int) const; - void setPiValue (int, double); - double* getLambdaValues (void) const; - double getLambdaValue (int) const; - void setLambdaValue (int, double); - double* getPiMessages (BpNode*) const; - double getPiMessage (BpNode*, int) const; - void setPiMessage (BpNode*, int, double); - double* getLambdaMessages (BpNode*) const; - double getLambdaMessage (BpNode*, int) const; - void setLambdaMessage (BpNode*, int, double); - double* getBeliefs (void); - double getBeliefChange (void); - void normalizeMessages (void); - void swapMessages (void); + BpNode (int); + BpNode (BayesNode*); + + ParamSet getBeliefs (void) const; + double getPiValue (int) const; + void setPiValue (int, double); + double getLambdaValue (int) const; + void setLambdaValue (int, double); + ParamSet& getPiValues (void); + ParamSet& getLambdaValues (void); + double getPiMessageValue (const BayesNode*, int) const; + double getLambdaMessageValue (const BayesNode*, int) const; + const ParamSet& getPiMessage (const BayesNode*) const; + const ParamSet& getLambdaMessage (const BayesNode*) const; + ParamSet& piNextMessageReference (const BayesNode*); + ParamSet& lambdaNextMessageReference (const BayesNode*); + void updatePiMessage (const BayesNode*); + void updateLambdaMessage (const BayesNode*); + double getBeliefChange (void); + void updatePiResidual (const BayesNode*); + void updateLambdaResidual (const BayesNode*); + void clearPiResidual (const BayesNode*); + void clearLambdaResidual (const BayesNode*); + bool hasReceivedChildInfluence (void) const; + // inlines + double getPiResidual (const BayesNode*); + double getLambdaResidual (const BayesNode*); + int getIndex (const BayesNode*) const; private: - BpNode (const BpNode&); // disallow copy - void operator= (const BpNode&); // disallow assign - // members - double* lambdaValues_; - double* piValues_; - map piMessages_; - map lambdaMessages_; - map* newPiMessages_; - map* newLambdaMessages_; - double* oldBeliefs_; - static bool parallelSchedule_; - static const double MAX_CHANGE_ = 1.0; + DISALLOW_COPY_AND_ASSIGN (BpNode); + + IndexMap indexMap_; + ParamSet piVals_; // pi values + ParamSet ldVals_; // lambda values + vector currPiMsgs_; // current pi messages + vector currLdMsgs_; // current lambda messages + vector nextPiMsgs_; + vector nextLdMsgs_; + ParamSet oldBeliefs_; + ParamSet piResiduals_; + ParamSet ldResiduals_; + int ds_; + const NodeSet* childs_; + static bool calculateMessageResidual_; +// static const double MAX_CHANGE_ = 10000000.0; }; -#endif // BP_BP_NODE_H + + +inline double +BpNode::getPiResidual (const BayesNode* destination) +{ + return piResiduals_[getIndex(destination)]; +} + + +inline double +BpNode::getLambdaResidual (const BayesNode* source) +{ + return ldResiduals_[getIndex(source)]; +} + + + +inline int +BpNode::getIndex (const BayesNode* node) const +{ + assert (node); + //assert (indexMap_.find(node->getVarId()) != indexMap_.end()); + //return indexMap_.find(node->getVarId())->second; + for (unsigned i = 0; childs_->size(); i++) { + if ((*childs_)[i]->getVarId() == node->getVarId()) { + return i; + } + } + assert (false); + return -1; +} + + +#endif diff --git a/packages/CLPBN/clpbn/bp/CptEntry.h b/packages/CLPBN/clpbn/bp/CptEntry.h index 7289212d7..9229b2564 100644 --- a/packages/CLPBN/clpbn/bp/CptEntry.h +++ b/packages/CLPBN/clpbn/bp/CptEntry.h @@ -1,23 +1,71 @@ -#ifndef CPT_ENTRY_H -#define CPT_ENTRY_H +#ifndef BP_CPTENTRY_H +#define BP_CPTENTRY_H #include +#include "Shared.h" + using namespace std; class CptEntry { public: - // constructs - CptEntry (int, vector); - // methods - int getCptIndex (void) const; - vector getDomainInstantiations (void) const; - bool matchConstraints (const vector >&) const; + CptEntry (unsigned, const vector&); + + unsigned getParameterIndex (void) const; + const vector& getParentConfigurations (void) const; + bool matchConstraints (const DomainConstr&) const; + bool matchConstraints (const vector&) const; + private: - // members - int cptIndex_; - vector instantiations_; + unsigned index_; + vector confs_; }; -#endif // CPT_ENTRY_H + + +inline +CptEntry::CptEntry (unsigned index, const vector& confs) +{ + index_ = index; + confs_ = confs; +} + + + +inline unsigned +CptEntry::getParameterIndex (void) const +{ + return index_; +} + + + +inline const vector& +CptEntry::getParentConfigurations (void) const +{ + return confs_; +} + + + +inline bool +CptEntry::matchConstraints (const DomainConstr& constr) const +{ + return confs_[constr.first] == constr.second; +} + + + +inline bool +CptEntry::matchConstraints (const vector& constrs) const +{ + for (unsigned j = 0; j < constrs.size(); j++) { + if (confs_[constrs[j].first] != constrs[j].second) { + return false; + } + } + return true; +} + +#endif diff --git a/packages/CLPBN/clpbn/bp/Distribution.h b/packages/CLPBN/clpbn/bp/Distribution.h index 51a540b4c..63b562be4 100644 --- a/packages/CLPBN/clpbn/bp/Distribution.h +++ b/packages/CLPBN/clpbn/bp/Distribution.h @@ -1,24 +1,40 @@ -#ifndef DISTRIBUTION_H -#define DISTRIBUTION_H +#ifndef BP_DISTRIBUTION_H +#define BP_DISTRIBUTION_H #include #include +#include "Shared.h" + using namespace std; -class CptEntry; - -class Distribution +struct Distribution { public: - Distribution (int, double*, int, vector); - Distribution (double*, int, vector); - int id; - double* params; - int nParams; - vector domain; - int* offsets; + Distribution (unsigned id) + { + this->id = id; + this->params = params; + } + + Distribution (const ParamSet& params) + { + this->id = -1; + this->params = params; + } + + void updateParameters (const ParamSet& params) + { + this->params = params; + } + + unsigned id; + ParamSet params; + vector entries; + + private: + DISALLOW_COPY_AND_ASSIGN (Distribution); }; -#endif // DISTRIBUTION +#endif diff --git a/packages/CLPBN/clpbn/bp/Factor.cpp b/packages/CLPBN/clpbn/bp/Factor.cpp new file mode 100755 index 000000000..66d2296e0 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/Factor.cpp @@ -0,0 +1,346 @@ +#include +#include +#include +#include + +#include "Factor.h" +#include "FgVarNode.h" + + +int Factor::indexCount_ = 0; + +Factor::Factor (FgVarNode* var) { + vs_.push_back (var); + int nParams = var->getDomainSize(); + // create a uniform distribution + double val = 1.0 / nParams; + ps_ = ParamSet (nParams, val); + id_ = indexCount_; + indexCount_ ++; +} + + + +Factor::Factor (const FgVarSet& vars) { + vs_ = vars; + int nParams = 1; + for (unsigned i = 0; i < vs_.size(); i++) { + nParams *= vs_[i]->getDomainSize(); + } + // create a uniform distribution + double val = 1.0 / nParams; + ps_ = ParamSet (nParams, val); + id_ = indexCount_; + indexCount_ ++; +} + + + +Factor::Factor (FgVarNode* var, + const ParamSet& params) +{ + vs_.push_back (var); + ps_ = params; + id_ = indexCount_; + indexCount_ ++; +} + + + +Factor::Factor (const FgVarSet& vars, + const ParamSet& params) +{ + vs_ = vars; + ps_ = params; + id_ = indexCount_; + indexCount_ ++; +} + + + +const FgVarSet& +Factor::getFgVarNodes (void) const +{ + return vs_; +} + + + +FgVarSet& +Factor::getFgVarNodes (void) +{ + return vs_; +} + + + +const ParamSet& +Factor::getParameters (void) const +{ + return ps_; +} + + + +ParamSet& +Factor::getParameters (void) +{ + return ps_; +} + + + +void +Factor::setParameters (const ParamSet& params) +{ + //cout << "ps size: " << ps_.size() << endl; + //cout << "params size: " << params.size() << endl; + assert (ps_.size() == params.size()); + ps_ = params; +} + + + +Factor& +Factor::operator= (const Factor& g) +{ + FgVarSet vars = g.getFgVarNodes(); + ParamSet params = g.getParameters(); + return *this; +} + + + +Factor& +Factor::operator*= (const Factor& g) +{ + FgVarSet gVs = g.getFgVarNodes(); + const ParamSet& gPs = g.getParameters(); + + bool hasCommonVars = false; + vector varIndexes; + for (unsigned i = 0; i < gVs.size(); i++) { + int idx = getIndexOf (gVs[i]); + if (idx == -1) { + insertVariable (gVs[i]); + varIndexes.push_back (vs_.size() - 1); + } else { + hasCommonVars = true; + varIndexes.push_back (idx); + } + } + + if (hasCommonVars) { + vector offsets (gVs.size()); + offsets[gVs.size() - 1] = 1; + for (int i = gVs.size() - 2; i >= 0; i--) { + offsets[i] = offsets[i + 1] * gVs[i + 1]->getDomainSize(); + } + vector entries = getCptEntries(); + for (unsigned i = 0; i < entries.size(); i++) { + int idx = 0; + const DomainConf conf = entries[i].getParentConfigurations(); + for (unsigned j = 0; j < varIndexes.size(); j++) { + idx += offsets[j] * conf[varIndexes[j]]; + } + //cout << "ps_[" << i << "] = " << ps_[i] << " * " ; + //cout << gPs[idx] << " , idx = " << idx << endl; + ps_[i] = ps_[i] * gPs[idx]; + } + } else { + // if the originally factors doesn't have common factors. + // we don't have to make domain comparations + unsigned idx = 0; + for (unsigned i = 0; i < ps_.size(); i++) { + //cout << "ps_[" << i << "] = " << ps_[i] << " * " ; + //cout << gPs[idx] << " , idx = " << idx << endl; + ps_[i] = ps_[i] * gPs[idx]; + idx ++; + if (idx >= gPs.size()) { + idx = 0; + } + } + } + return *this; +} + + + +void +Factor::insertVariable (FgVarNode* var) +{ + int c = 0; + ParamSet newPs (ps_.size() * var->getDomainSize()); + for (unsigned i = 0; i < ps_.size(); i++) { + for (int j = 0; j < var->getDomainSize(); j++) { + newPs[c] = ps_[i]; + c ++; + } + } + vs_.push_back (var); + ps_ = newPs; +} + + + +void +Factor::marginalizeVariable (const FgVarNode* var) { + int varIndex = getIndexOf (var); + marginalizeVariable (varIndex); +} + + + +void +Factor::marginalizeVariable (unsigned varIndex) +{ + assert (varIndex >= 0 && varIndex < vs_.size()); + int distOffset = 1; + int leftVarOffset = 1; + for (unsigned i = vs_.size() - 1; i > varIndex; i--) { + distOffset *= vs_[i]->getDomainSize(); + leftVarOffset *= vs_[i]->getDomainSize(); + } + leftVarOffset *= vs_[varIndex]->getDomainSize(); + + int ds = vs_[varIndex]->getDomainSize(); + int count = 0; + int offset = 0; + int startIndex = 0; + int currDomainIdx = 0; + unsigned newPsSize = ps_.size() / ds; + ParamSet newPs; + newPs.reserve (newPsSize); + + stringstream ss; + ss << "marginalizing " << vs_[varIndex]->getLabel(); + ss << " from factor " << getLabel() << endl; + while (newPs.size() < newPsSize) { + ss << " sum = "; + double sum = 0.0; + for (int j = 0; j < ds; j++) { + if (j != 0) ss << " + "; + ss << ps_[offset]; + sum = sum + ps_[offset]; + offset = offset + distOffset; + } + newPs.push_back (sum); + count ++; + if (varIndex == vs_.size() - 1) { + offset = count * ds; + } else { + offset = offset - distOffset + 1; + if ((offset % leftVarOffset) == 0) { + currDomainIdx ++; + startIndex = leftVarOffset * currDomainIdx; + offset = startIndex; + count = 0; + } else { + offset = startIndex + count; + } + } + ss << " = " << sum << endl; + } + //cout << ss.str() << endl; + ps_ = newPs; + vs_.erase (vs_.begin() + varIndex); +} + + + +string +Factor::getLabel (void) const +{ + stringstream ss; + ss << "f(" ; + // ss << "Φ(" ; + for (unsigned i = 0; i < vs_.size(); i++) { + if (i != 0) ss << ", " ; + ss << "v" << vs_[i]->getVarId(); + } + ss << ")" ; + return ss.str(); +} + + + +string +Factor::toString (void) const +{ + stringstream ss; + ss << "vars: " ; + for (unsigned i = 0; i < vs_.size(); i++) { + if (i != 0) ss << ", " ; + ss << "v" << vs_[i]->getVarId(); + } + ss << endl; + vector entries = getCptEntries(); + for (unsigned i = 0; i < entries.size(); i++) { + ss << "Φ(" ; + char s = 'a' ; + const DomainConf& conf = entries[i].getParentConfigurations(); + for (unsigned j = 0; j < conf.size(); j++) { + if (j != 0) ss << "," ; + ss << s << conf[j] + 1; + s++; + } + ss << ") = " << ps_[entries[i].getParameterIndex()] << endl; + } + return ss.str(); +} + + + +vector +Factor::getCptEntries (void) const +{ + vector confs (ps_.size()); + for (unsigned i = 0; i < ps_.size(); i++) { + confs[i].resize (vs_.size()); + } + + int nReps = 1; + for (int i = vs_.size() - 1; i >= 0; i--) { + unsigned index = 0; + while (index < ps_.size()) { + for (int j = 0; j < vs_[i]->getDomainSize(); j++) { + for (int r = 0; r < nReps; r++) { + confs[index][i] = j; + index++; + } + } + } + nReps *= vs_[i]->getDomainSize(); + } + + vector entries; + for (unsigned i = 0; i < ps_.size(); i++) { + for (unsigned j = 0; j < vs_.size(); j++) { + } + entries.push_back (CptEntry (i, confs[i])); + } + return entries; +} + + + +int +Factor::getIndexOf (const FgVarNode* var) const +{ + for (unsigned i = 0; i < vs_.size(); i++) { + if (vs_[i] == var) { + return i; + } + } + return -1; +} + + + +Factor operator* (const Factor& f, const Factor& g) +{ + Factor r = f; + r *= g; + return r; +} + diff --git a/packages/CLPBN/clpbn/bp/Factor.h b/packages/CLPBN/clpbn/bp/Factor.h new file mode 100755 index 000000000..71b14df07 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/Factor.h @@ -0,0 +1,45 @@ +#ifndef BP_FACTOR_H +#define BP_FACTOR_H + +#include + +#include "CptEntry.h" + +using namespace std; + +class FgVarNode; + +class Factor +{ + public: + Factor (FgVarNode*); + Factor (const FgVarSet&); + Factor (FgVarNode*, const ParamSet&); + Factor (const FgVarSet&, const ParamSet&); + + const FgVarSet& getFgVarNodes (void) const; + FgVarSet& getFgVarNodes (void); + const ParamSet& getParameters (void) const; + ParamSet& getParameters (void); + void setParameters (const ParamSet&); + Factor& operator= (const Factor& f); + Factor& operator*= (const Factor& f); + void insertVariable (FgVarNode* index); + void marginalizeVariable (const FgVarNode* var); + void marginalizeVariable (unsigned); + string getLabel (void) const; + string toString (void) const; + + private: + vector getCptEntries() const; + int getIndexOf (const FgVarNode*) const; + + FgVarSet vs_; + ParamSet ps_; + int id_; + static int indexCount_; +}; + +Factor operator* (const Factor&, const Factor&); + +#endif diff --git a/packages/CLPBN/clpbn/bp/FactorGraph.cpp b/packages/CLPBN/clpbn/bp/FactorGraph.cpp new file mode 100755 index 000000000..0a85d2b5b --- /dev/null +++ b/packages/CLPBN/clpbn/bp/FactorGraph.cpp @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include + +#include "FactorGraph.h" +#include "FgVarNode.h" +#include "Factor.h" + + +FactorGraph::FactorGraph (const char* fileName) +{ + string line; + ifstream is (fileName); + if (!is.is_open()) { + cerr << "error: cannot read from file " + std::string (fileName) << endl; + abort(); + } + + while (is.peek() == '#' || is.peek() == '\n') getline (is, line); + getline (is, line); + if (line != "MARKOV") { + cerr << "error: the network must be a MARKOV network " << endl; + abort(); + } + + while (is.peek() == '#' || is.peek() == '\n') getline (is, line); + int nVars; + is >> nVars; + + while (is.peek() == '#' || is.peek() == '\n') getline (is, line); + vector domainSizes (nVars); + for (int i = 0; i < nVars; i++) { + int ds; + is >> ds; + domainSizes[i] = ds; + } + + while (is.peek() == '#' || is.peek() == '\n') getline (is, line); + for (int i = 0; i < nVars; i++) { + varNodes_.push_back (new FgVarNode (i, domainSizes[i])); + } + + int nFactors; + is >> nFactors; + for (int i = 0; i < nFactors; i++) { + while (is.peek() == '#' || is.peek() == '\n') getline (is, line); + int nFactorVars; + is >> nFactorVars; + FgVarSet factorVars; + for (int j = 0; j < nFactorVars; j++) { + int varId; + is >> varId; + FgVarNode* var = getVariableById (varId); + if (var == 0) { + cerr << "error: invalid variable identifier (" << varId << ")" << endl; + abort(); + } + factorVars.push_back (var); + } + Factor* f = new Factor (factorVars); + factors_.push_back (f); + for (unsigned j = 0; j < factorVars.size(); j++) { + factorVars[j]->addFactor (f); + } + } + + for (int i = 0; i < nFactors; i++) { + while (is.peek() == '#' || is.peek() == '\n') getline (is, line); + int nParams; + is >> nParams; + ParamSet params (nParams); + for (int j = 0; j < nParams; j++) { + double param; + is >> param; + params[j] = param; + } + factors_[i]->setParameters (params); + } + is.close(); + + for (unsigned i = 0; i < varNodes_.size(); i++) { + varNodes_[i]->setIndex (i); + } +} + + + +FactorGraph::~FactorGraph (void) +{ + for (unsigned i = 0; i < varNodes_.size(); i++) { + delete varNodes_[i]; + } + for (unsigned i = 0; i < factors_.size(); i++) { + delete factors_[i]; + } +} + + + +FgVarSet +FactorGraph::getFgVarNodes (void) const +{ + return varNodes_; +} + + + +vector +FactorGraph::getFactors (void) const +{ + return factors_; +} + + + +VarSet +FactorGraph::getVariables (void) const +{ + VarSet vars; + for (unsigned i = 0; i < varNodes_.size(); i++) { + vars.push_back (varNodes_[i]); + } + return vars; +} + + + +FgVarNode* +FactorGraph::getVariableById (unsigned id) const +{ + for (unsigned i = 0; i < varNodes_.size(); i++) { + if (varNodes_[i]->getVarId() == id) { + return varNodes_[i]; + } + } + return 0; +} + + + +FgVarNode* +FactorGraph::getVariableByLabel (string label) const +{ + for (unsigned i = 0; i < varNodes_.size(); i++) { + stringstream ss; + ss << "v" << varNodes_[i]->getVarId(); + if (ss.str() == label) { + return varNodes_[i]; + } + } + return 0; +} + + + +void +FactorGraph::printFactorGraph (void) const +{ + for (unsigned i = 0; i < varNodes_.size(); i++) { + cout << "variable number " << varNodes_[i]->getIndex() << endl; + cout << "Id = " << varNodes_[i]->getVarId() << endl; + cout << "Domain size = " << varNodes_[i]->getDomainSize() << endl; + cout << "Evidence = " << varNodes_[i]->getEvidence() << endl; + cout << endl; + } + cout << endl; + for (unsigned i = 0; i < factors_.size(); i++) { + cout << factors_[i]->toString() << endl; + } +} + diff --git a/packages/CLPBN/clpbn/bp/FactorGraph.h b/packages/CLPBN/clpbn/bp/FactorGraph.h new file mode 100755 index 000000000..9809f25b8 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/FactorGraph.h @@ -0,0 +1,35 @@ +#ifndef BP_FACTORGRAPH_H +#define BP_FACTORGRAPH_H + +#include +#include + +#include "GraphicalModel.h" +#include "Shared.h" + +using namespace std; + +class FgVarNode; +class Factor; + +class FactorGraph : public GraphicalModel +{ + public: + FactorGraph (const char* fileName); + ~FactorGraph (void); + + FgVarSet getFgVarNodes (void) const; + vector getFactors (void) const; + VarSet getVariables (void) const; + FgVarNode* getVariableById (unsigned) const; + FgVarNode* getVariableByLabel (string) const; + void printFactorGraph (void) const; + + private: + DISALLOW_COPY_AND_ASSIGN (FactorGraph); + + FgVarSet varNodes_; + vector factors_; +}; + +#endif diff --git a/packages/CLPBN/clpbn/bp/FgVarNode.h b/packages/CLPBN/clpbn/bp/FgVarNode.h new file mode 100755 index 000000000..e82ab0c52 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/FgVarNode.h @@ -0,0 +1,28 @@ +#ifndef BP_VARIABLE_H +#define BP_VARIABLE_H + +#include +#include + +#include "Variable.h" +#include "Shared.h" + +using namespace std; + +class Factor; + +class FgVarNode : public Variable +{ + public: + FgVarNode (int varId, int dsize) : Variable (varId, dsize) { } + + void addFactor (Factor* f) { factors_.push_back (f); } + vector getFactors (void) const { return factors_; } + + private: + DISALLOW_COPY_AND_ASSIGN (FgVarNode); + // members + vector factors_; +}; + +#endif // BP_VARIABLE_H diff --git a/packages/CLPBN/clpbn/bp/GraphicalModel.h b/packages/CLPBN/clpbn/bp/GraphicalModel.h new file mode 100755 index 000000000..4aaf4baf3 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/GraphicalModel.h @@ -0,0 +1,17 @@ +#ifndef BP_GRAPHICALMODEL_H +#define BP_GRAPHICALMODEL_H + +#include "Variable.h" +#include "Shared.h" + +using namespace std; + +class GraphicalModel +{ + public: + virtual VarSet getVariables (void) const = 0; + + private: +}; + +#endif diff --git a/packages/CLPBN/clpbn/bp/HorusCli.cpp b/packages/CLPBN/clpbn/bp/HorusCli.cpp new file mode 100755 index 000000000..a4619566f --- /dev/null +++ b/packages/CLPBN/clpbn/bp/HorusCli.cpp @@ -0,0 +1,214 @@ +#include +#include +#include + +#include "BayesNet.h" +#include "BPSolver.h" + +#include "FactorGraph.h" +#include "SPSolver.h" + +using namespace std; + +void BayesianNetwork (int, const char* []); +void markovNetwork (int, const char* []); + +const string USAGE = "usage: \ +./hcli FILE [VARIABLE | OBSERVED_VARIABLE=EVIDENCE]..." ; + + +int +main (int argc, const char* argv[]) +{ + if (!argv[1]) { + cerr << "error: no graphical model specified" << endl; + cerr << USAGE << endl; + exit (0); + } + string fileName = argv[1]; + string extension = fileName.substr (fileName.find_last_of ('.') + 1); + if (extension == "xml") { + BayesianNetwork (argc, argv); + } else if (extension == "uai") { + markovNetwork (argc, argv); + } else { + cerr << "error: the graphical model must be defined either " ; + cerr << "in a xml file or uai file" << endl; + exit (0); + } + return 0; +} + + + +void +BayesianNetwork (int argc, const char* argv[]) +{ + BayesNet bn (argv[1]); + //bn.printNetwork(); + + NodeSet queryVars; + for (int i = 2; i < argc; i++) { + string arg = argv[i]; + if (arg.find ('=') == std::string::npos) { + BayesNode* queryVar = bn.getNode (arg); + if (queryVar) { + queryVars.push_back (queryVar); + } else { + cerr << "error: there isn't a variable labeled of " ; + cerr << "`" << arg << "'" ; + cerr << endl; + exit (0); + } + } else { + size_t pos = arg.find ('='); + string label = arg.substr (0, pos); + string state = arg.substr (pos + 1); + if (label.empty()) { + cerr << "error: missing left argument" << endl; + cerr << USAGE << endl; + exit (0); + } + if (state.empty()) { + cerr << "error: missing right argument" << endl; + cerr << USAGE << endl; + exit (0); + } + BayesNode* node = bn.getNode (label); + if (node) { + if (node->isValidState (state)) { + node->setEvidence (state); + } else { + cerr << "error: `" << state << "' " ; + cerr << "is not a valid state for " ; + cerr << "`" << node->getLabel() << "'" ; + cerr << endl; + exit (0); + } + } else { + cerr << "error: there isn't a variable labeled of " ; + cerr << "`" << label << "'" ; + cerr << endl; + exit (0); + } + } + } + + BPSolver solver (bn); + if (queryVars.size() == 0) { + solver.runSolver(); + solver.printAllPosterioris(); + } else if (queryVars.size() == 1) { + solver.runSolver(); + solver.printPosterioriOf (queryVars[0]); + } else { + Domain domain = BayesNet::getInstantiations(queryVars); + ParamSet params = solver.getJointDistribution (queryVars); + for (unsigned i = 0; i < params.size(); i++) { + cout << domain[i] << "\t" << params[i] << endl; + } + } + bn.freeDistributions(); +} + + + +void +markovNetwork (int argc, const char* argv[]) +{ + FactorGraph fg (argv[1]); + //fg.printFactorGraph(); + + VarSet queryVars; + for (int i = 2; i < argc; i++) { + string arg = argv[i]; + if (arg.find ('=') == std::string::npos) { + if (!Util::isInteger (arg)) { + cerr << "error: `" << arg << "' " ; + cerr << "is not a valid variable id" ; + cerr << endl; + exit (0); + } + unsigned varId; + stringstream ss; + ss << arg; + ss >> varId; + Variable* queryVar = fg.getVariableById (varId); + if (queryVar) { + queryVars.push_back (queryVar); + } else { + cerr << "error: there isn't a variable with " ; + cerr << "`" << varId << "' as id" ; + cerr << endl; + exit (0); + } + } else { + size_t pos = arg.find ('='); + if (arg.substr (0, pos).empty()) { + cerr << "error: missing left argument" << endl; + cerr << USAGE << endl; + exit (0); + } + if (arg.substr (pos + 1).empty()) { + cerr << "error: missing right argument" << endl; + cerr << USAGE << endl; + exit (0); + } + if (!Util::isInteger (arg.substr (0, pos))) { + cerr << "error: `" << arg.substr (0, pos) << "' " ; + cerr << "is not a variable id" ; + cerr << endl; + exit (0); + } + unsigned varId; + stringstream ss; + ss << arg.substr (0, pos); + ss >> varId; + Variable* var = fg.getVariableById (varId); + if (var) { + if (!Util::isInteger (arg.substr (pos + 1))) { + cerr << "error: `" << arg.substr (pos + 1) << "' " ; + cerr << "is not a state index" ; + cerr << endl; + exit (0); + } + int stateIndex; + stringstream ss; + ss << arg.substr (pos + 1); + ss >> stateIndex; + cout << "si: " << stateIndex << endl; + if (var->isValidStateIndex (stateIndex)) { + var->setEvidence (stateIndex); + } else { + cerr << "error: `" << stateIndex << "' " ; + cerr << "is not a valid state index for variable " ; + cerr << "`" << var->getVarId() << "'" ; + cerr << endl; + exit (0); + } + } else { + cerr << "error: there isn't a variable with " ; + cerr << "`" << varId << "' as id" ; + cerr << endl; + exit (0); + } + } + } + + SPSolver solver (fg); + if (queryVars.size() == 0) { + solver.runSolver(); + solver.printAllPosterioris(); + } else if (queryVars.size() == 1) { + solver.runSolver(); + solver.printPosterioriOf (queryVars[0]); + } else { + assert (false); //FIXME + //Domain domain = BayesNet::getInstantiations(queryVars); + //ParamSet params = solver.getJointDistribution (queryVars); + //for (unsigned i = 0; i < params.size(); i++) { + // cout << domain[i] << "\t" << params[i] << endl; + //} + } +} + diff --git a/packages/CLPBN/clpbn/bp/HorusYap.cpp b/packages/CLPBN/clpbn/bp/HorusYap.cpp new file mode 100755 index 000000000..eb2cbfa95 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/HorusYap.cpp @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include + +#include + +#include "callgrind.h" + +#include "BayesNet.h" +#include "BayesNode.h" +#include "BPSolver.h" + +using namespace std; + +int +createNetwork (void) +{ + Statistics::numCreatedNets ++; + cout << "creating network number " << Statistics::numCreatedNets << endl; + if (Statistics::numCreatedNets == 1) { + //CALLGRIND_START_INSTRUMENTATION; + } + BayesNet* bn = new BayesNet(); + + YAP_Term varList = YAP_ARG1; + while (varList != YAP_TermNil()) { + YAP_Term var = YAP_HeadOfTerm (varList); + unsigned varId = (unsigned) YAP_IntOfTerm (YAP_ArgOfTerm (1, var)); + unsigned dsize = (unsigned) YAP_IntOfTerm (YAP_ArgOfTerm (2, var)); + int evidence = (int) YAP_IntOfTerm (YAP_ArgOfTerm (3, var)); + YAP_Term parentL = YAP_ArgOfTerm (4, var); + unsigned distId = (unsigned) YAP_IntOfTerm (YAP_ArgOfTerm (5, var)); + NodeSet parents; + while (parentL != YAP_TermNil()) { + unsigned parentId = (unsigned) YAP_IntOfTerm (YAP_HeadOfTerm (parentL)); + BayesNode* parent = bn->getNode (parentId); + if (!parent) { + parent = bn->addNode (parentId); + } + parents.push_back (parent); + parentL = YAP_TailOfTerm (parentL); + } + Distribution* dist = bn->getDistribution (distId); + if (!dist) { + dist = new Distribution (distId); + bn->addDistribution (dist); + } + BayesNode* node = bn->getNode (varId); + if (node) { + node->setData (dsize, evidence, parents, dist); + } else { + bn->addNode (varId, dsize, evidence, parents, dist); + } + varList = YAP_TailOfTerm (varList); + } + bn->setIndexes(); + + if (Statistics::numCreatedNets == 1688) { + Statistics::writeStats(); + //Statistics::writeStats(); + //CALLGRIND_STOP_INSTRUMENTATION; + //CALLGRIND_DUMP_STATS; + //exit (0); + } + YAP_Int p = (YAP_Int) (bn); + return YAP_Unify (YAP_MkIntTerm (p), YAP_ARG2); +} + + + +int +setExtraVarsInfo (void) +{ + BayesNet* bn = (BayesNet*) YAP_IntOfTerm (YAP_ARG1); + YAP_Term varsInfoL = YAP_ARG2; + while (varsInfoL != YAP_TermNil()) { + YAP_Term head = YAP_HeadOfTerm (varsInfoL); + unsigned varId = YAP_IntOfTerm (YAP_ArgOfTerm (1, head)); + YAP_Atom label = YAP_AtomOfTerm (YAP_ArgOfTerm (2, head)); + YAP_Term domainL = YAP_ArgOfTerm (3, head); + Domain domain; + while (domainL != YAP_TermNil()) { + YAP_Atom atom = YAP_AtomOfTerm (YAP_HeadOfTerm (domainL)); + domain.push_back ((char*) YAP_AtomName (atom)); + domainL = YAP_TailOfTerm (domainL); + } + BayesNode* node = bn->getNode (varId); + assert (node); + node->setLabel ((char*) YAP_AtomName (label)); + node->setDomain (domain); + varsInfoL = YAP_TailOfTerm (varsInfoL); + } + return TRUE; +} + + + +int +setParameters (void) +{ + BayesNet* bn = (BayesNet*) YAP_IntOfTerm (YAP_ARG1); + YAP_Term distList = YAP_ARG2; + while (distList != YAP_TermNil()) { + YAP_Term dist = YAP_HeadOfTerm (distList); + unsigned distId = (unsigned) YAP_IntOfTerm (YAP_ArgOfTerm (1, dist)); + YAP_Term paramL = YAP_ArgOfTerm (2, dist); + ParamSet params; + while (paramL!= YAP_TermNil()) { + params.push_back ((double) YAP_FloatOfTerm (YAP_HeadOfTerm (paramL))); + paramL = YAP_TailOfTerm (paramL); + } + bn->getDistribution(distId)->updateParameters(params); + distList = YAP_TailOfTerm (distList); + } + return TRUE; +} + + + +int +runSolver (void) +{ + BayesNet* bn = (BayesNet*) YAP_IntOfTerm (YAP_ARG1); + YAP_Term taskList = YAP_ARG2; + + vector tasks; + NodeSet marginalVars; + + while (taskList != YAP_TermNil()) { + if (YAP_IsPairTerm (YAP_HeadOfTerm (taskList))) { + NodeSet jointVars; + YAP_Term jointList = YAP_HeadOfTerm (taskList); + while (jointList != YAP_TermNil()) { + unsigned varId = (unsigned) YAP_IntOfTerm (YAP_HeadOfTerm (jointList)); + assert (bn->getNode (varId)); + jointVars.push_back (bn->getNode (varId)); + jointList = YAP_TailOfTerm (jointList); + } + tasks.push_back (jointVars); + } else { + unsigned varId = (unsigned) YAP_IntOfTerm (YAP_HeadOfTerm (taskList)); + BayesNode* node = bn->getNode (varId); + assert (node); + tasks.push_back (NodeSet() = {node}); + marginalVars.push_back (node); + } + taskList = YAP_TailOfTerm (taskList); + } + /* + cout << "tasks to resolve:" << endl; + for (unsigned i = 0; i < tasks.size(); i++) { + cout << "i" << ": " ; + if (tasks[i].size() == 1) { + cout << tasks[i][0]->getVarId() << endl; + } else { + for (unsigned j = 0; j < tasks[i].size(); j++) { + cout << tasks[i][j]->getVarId() << " " ; + } + cout << endl; + } + } + */ + + cerr << "prunning now..." << endl; + BayesNet* prunedNet = bn->pruneNetwork (marginalVars); + bn->printNetworkToFile ("net.txt"); + BPSolver solver (*prunedNet); + cerr << "solving marginals now..." << endl; + solver.runSolver(); + cerr << "calculating joints now ..." << endl; + + vector results; + results.reserve (tasks.size()); + for (unsigned i = 0; i < tasks.size(); i++) { + if (tasks[i].size() == 1) { + BayesNode* node = prunedNet->getNode (tasks[i][0]->getVarId()); + results.push_back (solver.getPosterioriOf (node)); + } else { + BPSolver solver2 (*bn); + cout << "calculating an join dist on: " ; + for (unsigned j = 0; j < tasks[i].size(); j++) { + cout << tasks[i][j]->getVarId() << " " ; + } + cout << "..." << endl; + results.push_back (solver2.getJointDistribution (tasks[i])); + } + } + + delete prunedNet; + + YAP_Term list = YAP_TermNil(); + for (int i = results.size() - 1; i >= 0; i--) { + const ParamSet& beliefs = results[i]; + YAP_Term queryBeliefsL = YAP_TermNil(); + for (int j = beliefs.size() - 1; j >= 0; j--) { + YAP_Term belief = YAP_MkFloatTerm (beliefs[j]); + queryBeliefsL = YAP_MkPairTerm (belief, queryBeliefsL); + } + list = YAP_MkPairTerm (queryBeliefsL, list); + } + + return YAP_Unify (list, YAP_ARG3); +} + + + +int +deleteBayesNet (void) +{ + BayesNet* bn = (BayesNet*) YAP_IntOfTerm (YAP_ARG1); + bn->freeDistributions(); + delete bn; + return TRUE; +} + + + +extern "C" void +init_predicates (void) +{ + YAP_UserCPredicate ("create_network", createNetwork, 2); + YAP_UserCPredicate ("set_extra_vars_info", setExtraVarsInfo, 2); + YAP_UserCPredicate ("set_parameters", setParameters, 2); + YAP_UserCPredicate ("run_solver", runSolver, 3); + YAP_UserCPredicate ("delete_bayes_net", deleteBayesNet, 1); +} + diff --git a/packages/CLPBN/clpbn/bp/Makefile.in b/packages/CLPBN/clpbn/bp/Makefile.in index 201e1e76b..056fab0b8 100755 --- a/packages/CLPBN/clpbn/bp/Makefile.in +++ b/packages/CLPBN/clpbn/bp/Makefile.in @@ -21,7 +21,17 @@ YAPLIBDIR=@libdir@/Yap # CC=@CC@ CXX=@CXX@ -CXXFLAGS= @SHLIB_CXXFLAGS@ $(YAP_EXTRAS) $(DEFS) -D_YAP_NOT_INSTALLED_=1 -I$(srcdir) -I../../../.. -I$(srcdir)/../../../../include @CPPFLAGS@ + +# normal +CXXFLAGS= -std=c++0x @SHLIB_CXXFLAGS@ $(YAP_EXTRAS) $(DEFS) -D_YAP_NOT_INSTALLED_=1 -I$(srcdir) -I../../../.. -I$(srcdir)/../../../../include @CPPFLAGS@ -DNDEBUG + +# debug +#CXXFLAGS= -std=c++0x @SHLIB_CXXFLAGS@ $(YAP_EXTRAS) $(DEFS) -D_YAP_NOT_INSTALLED_=1 -I$(srcdir) -I../../../.. -I$(srcdir)/../../../../include @CPPFLAGS@ -g -O0 + +# profiling (callgrind) +#CXXFLAGS= -std=c++0x @SHLIB_CXXFLAGS@ $(YAP_EXTRAS) $(DEFS) -D_YAP_NOT_INSTALLED_=1 -I$(srcdir) -I../../../.. -I$(srcdir)/../../../../include @CPPFLAGS@ -g -DNDEBUG + + # # # You shouldn't need to change what follows. @@ -38,64 +48,75 @@ CWD=$(PWD) HEADERS = \ - $(srcdir)/BayesianNetwork.h \ - $(srcdir)/BayesianNode.h \ - $(srcdir)/BpNetwork.h \ - $(srcdir)/BpNode.h \ + $(srcdir)/GraphicalModel.h \ + $(srcdir)/Variable.h \ + $(srcdir)/BayesNet.h \ + $(srcdir)/BayesNode.h \ $(srcdir)/Distribution.h \ $(srcdir)/CptEntry.h \ - $(srcdir)/BifInterface.h \ + $(srcdir)/FactorGraph.h \ + $(srcdir)/FgVarNode.h \ + $(srcdir)/Factor.h \ + $(srcdir)/Solver.h \ + $(srcdir)/BPSolver.h \ + $(srcdir)/BpNode.h \ + $(srcdir)/SPSolver.h \ + $(srcdir)/Shared.h \ $(srcdir)/xmlParser/xmlParser.h - + CPP_SOURCES = \ - $(srcdir)/BayesianNetwork.cpp \ - $(srcdir)/BayesianNode.cpp \ - $(srcdir)/BpNetwork.cpp \ + $(srcdir)/BayesNet.cpp \ + $(srcdir)/BayesNode.cpp \ + $(srcdir)/FactorGraph.cpp \ + $(srcdir)/Factor.cpp \ + $(srcdir)/BPSolver.cpp \ $(srcdir)/BpNode.cpp \ - $(srcdir)/Distribution.cpp \ - $(srcdir)/CptEntry.cpp \ - $(srcdir)/Horus.cpp \ - $(srcdir)/BifInterface.cpp \ - $(srcdir)/BifTest.cpp \ + $(srcdir)/SPSolver.cpp \ + $(srcdir)/HorusYap.cpp \ + $(srcdir)/HorusCli.cpp \ $(srcdir)/xmlParser/xmlParser.cpp OBJS = \ - BayesianNetwork.o \ - BayesianNode.o \ - BpNetwork.o \ + BayesNet.o \ + BayesNode.o \ + FactorGraph.o \ + Factor.o \ + BPSolver.o \ BpNode.o \ - Distribution.o \ - CptEntry.o \ - Horus.o + SPSolver.o \ + HorusYap.o -BIF_OBJS = \ - BayesianNetwork.o \ - BayesianNode.o \ - BpNetwork.o \ - BpNode.o \ - Distribution.o \ - CptEntry.o \ - BifInterface.o \ - BifTest.o \ - xmlParser.o +HCLI_OBJS = \ + BayesNet.o \ + BayesNode.o \ + FactorGraph.o \ + Factor.o \ + BPSolver.o \ + BpNode.o \ + SPSolver.o \ + HorusCli.o \ + xmlParser/xmlParser.o SOBJS=horus.@SO@ -all: $(SOBJS) biftest +all: $(SOBJS) hcli # default rule +%.o : $(srcdir)/%.cpp + $(CXX) -c $(CXXFLAGS) $< -o $@ + + xmlParser.o : $(srcdir)/xmlParser/xmlParser.cpp $(CXX) -c $(CXXFLAGS) $< -o $@ -%.o : $(srcdir)/%.cpp - $(CXX) -c $(CXXFLAGS) $< -o $@ @DO_SECOND_LD@horus.@SO@: $(OBJS) @DO_SECOND_LD@ @SHLIB_CXX_LD@ -o horus.@SO@ $(OBJS) @EXTRA_LIBS_FOR_SWIDLLS@ -biftest: $(BIF_OBJS) - $(CXX) -o biftest $(BIF_OBJS) + +hcli: $(HCLI_OBJS) + $(CXX) -o hcli $(HCLI_OBJS) install: all @@ -103,12 +124,12 @@ install: all clean: - rm -f *.o *~ $(OBJS) $(SOBJS) *.BAK biftest xmlParser/*.o + rm -f *.o *~ $(OBJS) $(SOBJS) *.BAK hcli xmlParser/*.o depend: $(HEADERS) $(CPP_SOURCES) -@if test "$(GCC)" = yes; then\ - $(CC) -MM -MG $(CFLAGS) -I$(srcdir) -I$(srcdir)/../../../../include -I$(srcdir)/../../../../H $(CPP_SOURCES) >> Makefile;\ + $(CC) -std=c++0x -MM -MG $(CFLAGS) -I$(srcdir) -I$(srcdir)/../../../../include -I$(srcdir)/../../../../H $(CPP_SOURCES) >> Makefile;\ else\ makedepend -f - -- $(CFLAGS) -I$(srcdir)/../../../../H -I$(srcdir)/../../../../include -- $(CPP_SOURCES) |\ sed 's|.*/\([^:]*\):|\1:|' >> Makefile ;\ diff --git a/packages/CLPBN/clpbn/bp/SPSolver.cpp b/packages/CLPBN/clpbn/bp/SPSolver.cpp new file mode 100755 index 000000000..fe0ea1e69 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/SPSolver.cpp @@ -0,0 +1,295 @@ +#include +#include +#include + +#include "SPSolver.h" +#include "FactorGraph.h" +#include "FgVarNode.h" +#include "Factor.h" + +SPSolver* Link::klass = 0; + + +SPSolver::SPSolver (const FactorGraph& fg) : Solver (&fg) +{ + fg_ = &fg; + accuracy_ = 0.0001; + maxIter_ = 10000; + //schedule_ = S_SEQ_FIXED; + //schedule_ = S_SEQ_RANDOM; + //schedule_ = S_SEQ_PARALLEL; + schedule_ = S_MAX_RESIDUAL; + Link::klass = this; + FgVarSet vars = fg_->getFgVarNodes(); + for (unsigned i = 0; i < vars.size(); i++) { + msgs_.push_back (new MessageBanket (vars[i])); + } +} + + + +SPSolver::~SPSolver (void) +{ + for (unsigned i = 0; i < msgs_.size(); i++) { + delete msgs_[i]; + } +} + + + +void +SPSolver::runSolver (void) +{ + nIter_ = 0; + vector factors = fg_->getFactors(); + for (unsigned i = 0; i < factors.size(); i++) { + FgVarSet neighbors = factors[i]->getFgVarNodes(); + for (unsigned j = 0; j < neighbors.size(); j++) { + updateOrder_.push_back (Link (factors[i], neighbors[j])); + } + } + + while (!converged() && nIter_ < maxIter_) { + if (DL >= 1) { + cout << endl; + cout << "****************************************" ; + cout << "****************************************" ; + cout << endl; + cout << " Iteration " << nIter_ + 1 << endl; + cout << "****************************************" ; + cout << "****************************************" ; + cout << endl; + } + + switch (schedule_) { + + case S_SEQ_RANDOM: + random_shuffle (updateOrder_.begin(), updateOrder_.end()); + // no break + + case S_SEQ_FIXED: + for (unsigned c = 0; c < updateOrder_.size(); c++) { + Link& link = updateOrder_[c]; + calculateNextMessage (link.source, link.destination); + updateMessage (updateOrder_[c]); + } + break; + + case S_PARALLEL: + for (unsigned c = 0; c < updateOrder_.size(); c++) { + Link link = updateOrder_[c]; + calculateNextMessage (link.source, link.destination); + } + for (unsigned c = 0; c < updateOrder_.size(); c++) { + Link link = updateOrder_[c]; + updateMessage (updateOrder_[c]); + } + break; + + case S_MAX_RESIDUAL: + maxResidualSchedule(); + break; + } + + nIter_++; + } + cout << endl; + if (DL >= 1) { + if (nIter_ < maxIter_) { + cout << "Loopy Sum-Product converged in " ; + cout << nIter_ << " iterations" << endl; + } else { + cout << "The maximum number of iterations was hit, terminating..." ; + cout << endl; + } + } +} + + + +ParamSet +SPSolver::getPosterioriOf (const Variable* var) const +{ + assert (var); + assert (var == fg_->getVariableById (var->getVarId())); + assert (var->getIndex() < msgs_.size()); + + ParamSet probs (var->getDomainSize(), 1); + if (var->hasEvidence()) { + for (unsigned i = 0; i < probs.size(); i++) { + if ((int)i != var->getEvidence()) { + probs[i] = 0; + } + } + + } else { + + MessageBanket* mb = msgs_[var->getIndex()]; + const FgVarNode* varNode = fg_->getFgVarNodes()[var->getIndex()]; + vector neighbors = varNode->getFactors(); + for (unsigned i = 0; i < neighbors.size(); i++) { + const Message& msg = mb->getMessage (neighbors[i]); + for (unsigned j = 0; j < msg.size(); j++) { + probs[j] *= msg[j]; + } + } + Util::normalize (probs); + } + + return probs; +} + + + +bool +SPSolver::converged (void) +{ + if (nIter_ == 0 || nIter_ == 1) { + return false; + } + bool converged = true; + for (unsigned i = 0; i < updateOrder_.size(); i++) { + double residual = getResidual (updateOrder_[i]); + if (DL >= 1) { + cout << updateOrder_[i].toString(); + cout << " residual = " << residual << endl; + } + if (residual > accuracy_) { + converged = false; + if (DL == 0) { + break; + } + } + } + return converged; +} + + + +void +SPSolver::maxResidualSchedule (void) +{ + if (nIter_ == 0) { + for (unsigned c = 0; c < updateOrder_.size(); c++) { + Link& l = updateOrder_[c]; + calculateNextMessage (l.source, l.destination); + if (DL >= 1) { + cout << updateOrder_[c].toString() << " residual = " ; + cout << getResidual (updateOrder_[c]) << endl; + } + } + sort (updateOrder_.begin(), updateOrder_.end(), compareResidual); + } else { + + for (unsigned c = 0; c < updateOrder_.size(); c++) { + Link& link = updateOrder_.front(); + updateMessage (link); + resetResidual (link); + + // update the messages that depend on message source --> destination + vector fstLevelNeighbors = link.destination->getFactors(); + for (unsigned i = 0; i < fstLevelNeighbors.size(); i++) { + if (fstLevelNeighbors[i] != link.source) { + FgVarSet sndLevelNeighbors; + sndLevelNeighbors = fstLevelNeighbors[i]->getFgVarNodes(); + for (unsigned j = 0; j < sndLevelNeighbors.size(); j++) { + if (sndLevelNeighbors[j] != link.destination) { + calculateNextMessage (fstLevelNeighbors[i], sndLevelNeighbors[j]); + } + } + } + } + sort (updateOrder_.begin(), updateOrder_.end(), compareResidual); + } + } +} + + + +void +SPSolver::updateMessage (const Link& link) +{ + updateMessage (link.source, link.destination); +} + + + +void +SPSolver::updateMessage (const Factor* src, const FgVarNode* dest) +{ + msgs_[dest->getIndex()]->updateMessage (src); +/* cout << src->getLabel() << " --> " << dest->getLabel() << endl; + cout << " m: " ; + Message msg = msgs_[dest->getIndex()]->getMessage (src); + for (unsigned i = 0; i < msg.size(); i++) { + if (i != 0) cout << ", " ; + cout << msg[i]; + } + cout << endl; +*/ +} + + + +void +SPSolver::calculateNextMessage (const Link& link) +{ + calculateNextMessage (link.source, link.destination); +} + + +void +SPSolver::calculateNextMessage (const Factor* src, const FgVarNode* dest) +{ + FgVarSet neighbors = src->getFgVarNodes(); + // calculate the product of MessageBankets sended + // to factor `src', except from var `dest' + Factor result = *src; + for (unsigned i = 0; i < neighbors.size(); i++) { + if (neighbors[i] != dest) { + Message msg (neighbors[i]->getDomainSize(), 1); + calculateVarFactorMessage (neighbors[i], src, msg); + result *= Factor (neighbors[i], msg); + } + } + // marginalize all vars except `dest' + for (unsigned i = 0; i < neighbors.size(); i++) { + if (neighbors[i] != dest) { + result.marginalizeVariable (neighbors[i]); + } + } + msgs_[dest->getIndex()]->setNextMessage (src, result.getParameters()); +} + + + +void +SPSolver::calculateVarFactorMessage (const FgVarNode* src, + const Factor* dest, + Message& placeholder) const +{ + assert (src->getDomainSize() == (int)placeholder.size()); + if (src->hasEvidence()) { + for (unsigned i = 0; i < placeholder.size(); i++) { + if ((int)i != src->getEvidence()) { + placeholder[i] = 0.0; + } else { + placeholder[i] = 1.0; + } + } + + } else { + + MessageBanket* mb = msgs_[src->getIndex()]; + vector neighbors = src->getFactors(); + for (unsigned i = 0; i < neighbors.size(); i++) { + if (neighbors[i] != dest) { + const Message& fromFactor = mb->getMessage (neighbors[i]); + for (unsigned j = 0; j < fromFactor.size(); j++) { + placeholder[j] *= fromFactor[j]; + } + } + } + } +} + diff --git a/packages/CLPBN/clpbn/bp/SPSolver.h b/packages/CLPBN/clpbn/bp/SPSolver.h new file mode 100755 index 000000000..421172166 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/SPSolver.h @@ -0,0 +1,171 @@ +#ifndef BP_SPSOLVER_H +#define BP_SPSOLVER_H + +#include +#include +#include +#include + +#include "Solver.h" +#include "FgVarNode.h" +#include "Factor.h" + +using namespace std; + +class FactorGraph; +class SPSolver; + +struct Link +{ + Link (Factor* s, FgVarNode* d) + { + source = s; + destination = d; + } + string toString (void) const + { + stringstream ss; + ss << source->getLabel() << " --> " ; + ss << destination->getLabel(); + return ss.str(); + } + Factor* source; + FgVarNode* destination; + static SPSolver* klass; +}; + + + +class MessageBanket +{ + public: + MessageBanket (const FgVarNode* var) + { + vector sources = var->getFactors(); + for (unsigned i = 0; i < sources.size(); i++) { + indexMap_.insert (make_pair (sources[i], i)); + currMsgs_.push_back (Message(var->getDomainSize(), 1)); + nextMsgs_.push_back (Message(var->getDomainSize(), -10)); + residuals_.push_back (0.0); + } + } + + void updateMessage (const Factor* source) + { + unsigned idx = getIndex(source); + currMsgs_[idx] = nextMsgs_[idx]; + } + + void setNextMessage (const Factor* source, const Message& msg) + { + unsigned idx = getIndex(source); + nextMsgs_[idx] = msg; + residuals_[idx] = computeResidual (source); + } + + const Message& getMessage (const Factor* source) const + { + return currMsgs_[getIndex(source)]; + } + + double getResidual (const Factor* source) const + { + return residuals_[getIndex(source)]; + } + + void resetResidual (const Factor* source) + { + residuals_[getIndex(source)] = 0.0; + } + + private: + double computeResidual (const Factor* source) + { + double change = 0.0; + unsigned idx = getIndex (source); + const Message& currMessage = currMsgs_[idx]; + const Message& nextMessage = nextMsgs_[idx]; + for (unsigned i = 0; i < currMessage.size(); i++) { + change += abs (currMessage[i] - nextMessage[i]); + } + return change; + } + + unsigned getIndex (const Factor* factor) const + { + assert (factor); + assert (indexMap_.find(factor) != indexMap_.end()); + return indexMap_.find(factor)->second; + } + + typedef map IndexMap; + + IndexMap indexMap_; + vector currMsgs_; + vector nextMsgs_; + vector residuals_; +}; + + + +class SPSolver : public Solver +{ + public: + SPSolver (const FactorGraph&); + ~SPSolver (void); + + void runSolver (void); + ParamSet getPosterioriOf (const Variable* var) const; + + private: + bool converged (void); + void maxResidualSchedule (void); + void updateMessage (const Link&); + void updateMessage (const Factor*, const FgVarNode*); + void calculateNextMessage (const Link&); + void calculateNextMessage (const Factor*, const FgVarNode*); + void calculateVarFactorMessage ( + const FgVarNode*, const Factor*, Message&) const; + double getResidual (const Link&) const; + void resetResidual (const Link&) const; + friend bool compareResidual (const Link&, const Link&); + + const FactorGraph* fg_; + vector msgs_; + Schedule schedule_; + int nIter_; + double accuracy_; + int maxIter_; + vector updateOrder_; +}; + + + +inline double +SPSolver::getResidual (const Link& link) const +{ + MessageBanket* mb = Link::klass->msgs_[link.destination->getIndex()]; + return mb->getResidual (link.source); +} + + + +inline void +SPSolver::resetResidual (const Link& link) const +{ + MessageBanket* mb = Link::klass->msgs_[link.destination->getIndex()]; + mb->resetResidual (link.source); +} + + + +inline bool +compareResidual (const Link& link1, const Link& link2) +{ + MessageBanket* mb1 = Link::klass->msgs_[link1.destination->getIndex()]; + MessageBanket* mb2 = Link::klass->msgs_[link2.destination->getIndex()]; + return mb1->getResidual(link1.source) > mb2->getResidual(link2.source); +} + +#endif + diff --git a/packages/CLPBN/clpbn/bp/Shared.h b/packages/CLPBN/clpbn/bp/Shared.h new file mode 100755 index 000000000..82afd3e51 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/Shared.h @@ -0,0 +1,203 @@ +#ifndef BP_SHARED_H +#define BP_SHARED_H + +#include +#include +#include +#include +#include +#include +#include + +// Macro to disallow the copy constructor and operator= functions +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +using namespace std; + +class Variable; +class BayesNode; +class FgVarNode; + +typedef double Param; +typedef vector ParamSet; +typedef vector Message; +typedef vector VarSet; +typedef vector NodeSet; +typedef vector FgVarSet; +typedef vector Domain; +typedef vector DomainConf; +typedef pair DomainConstr; +typedef unordered_map IndexMap; + + +//extern unsigned DL; +static const unsigned DL = 0; + +// number of digits to show when printing a parameter +static const unsigned PRECISION = 10; + +// shared by bp and sp solver +enum Schedule +{ + S_SEQ_FIXED, + S_SEQ_RANDOM, + S_PARALLEL, + S_MAX_RESIDUAL +}; + + +struct NetInfo +{ + NetInfo (unsigned c, double t) + { + counting = c; + solvingTime = t; + } + unsigned counting; + double solvingTime; +}; + +typedef map StatisticMap; + + +class Statistics +{ + public: + + static void updateStats (unsigned size, double time) + { + StatisticMap::iterator it = stats_.find(size); + if (it == stats_.end()) { + stats_.insert (make_pair (size, NetInfo (1, 0.0))); + } else { + it->second.counting ++; + it->second.solvingTime += time; + } + } + + static unsigned getCounting (unsigned size) + { + StatisticMap::iterator it = stats_.find(size); + assert (it != stats_.end()); + return it->second.counting; + } + + static void updateIterations (unsigned nIters) + { + totalOfIterations += nIters; + if (nIters > maxIterations) { + maxIterations = nIters; + } + } + + static void writeStats (void) + { + ofstream out ("../../stats.txt"); + if (!out.is_open()) { + cerr << "error: cannot open file to write at " ; + cerr << "Statistics:::updateStats()" << endl; + abort(); + } + unsigned avgIterations = 0; + if (numSolvedLoopyNets > 0) { + avgIterations = totalOfIterations / numSolvedLoopyNets; + } + double totalSolvingTime = 0.0; + for (StatisticMap::iterator it = stats_.begin(); + it != stats_.end(); it++) { + totalSolvingTime += it->second.solvingTime; + } + out << "created networks: " << numCreatedNets << endl; + out << "solver runs on polytrees: " << numSolvedPolyTrees << endl; + out << "solver runs on loopy networks: " << numSolvedLoopyNets << endl; + out << " unconverged: " << numUnconvergedRuns << endl; + out << " max iterations: " << maxIterations << endl; + out << " average iterations: " << avgIterations << endl; + out << "total solving time " << totalSolvingTime << endl; + out << endl; + out << "Network Size\tCounting\tSolving Time\tAverage Time" << endl; + for (StatisticMap::iterator it = stats_.begin(); + it != stats_.end(); it++) { + out << it->first; + out << "\t\t" << it->second.counting; + out << "\t\t" << it->second.solvingTime; + if (it->second.counting > 0) { + out << "\t\t" << it->second.solvingTime / it->second.counting; + } else { + out << "\t\t0.0" ; + } + out << endl; + } + out.close(); + } + + static unsigned numCreatedNets; + static unsigned numSolvedPolyTrees; + static unsigned numSolvedLoopyNets; + static unsigned numUnconvergedRuns; + + private: + static StatisticMap stats_; + static unsigned maxIterations; + static unsigned totalOfIterations; + +}; + + + +class Util +{ + public: + static void normalize (ParamSet& v) + { + double sum = 0.0; + for (unsigned i = 0; i < v.size(); i++) { + sum += v[i]; + } + assert (sum != 0.0); + for (unsigned i = 0; i < v.size(); i++) { + v[i] /= sum; + } + } + + static double getL1dist (const ParamSet& v1, const ParamSet& v2) + { + assert (v1.size() == v2.size()); + double dist = 0.0; + for (unsigned i = 0; i < v1.size(); i++) { + dist += abs (v1[i] - v2[i]); + } + return dist; + } + + static double getMaxNorm (const ParamSet& v1, const ParamSet& v2) + { + assert (v1.size() == v2.size()); + double max = 0.0; + for (unsigned i = 0; i < v1.size(); i++) { + double diff = abs (v1[i] - v2[i]); + if (diff > max) { + max = diff; + } + } + return max; + } + + static bool isInteger (const string& s) + { + stringstream ss1 (s); + stringstream ss2; + int integer; + ss1 >> integer; + ss2 << integer; + return (ss1.str() == ss2.str()); + } +}; + + +//unsigned Statistics::totalOfIterations = 0; + +#endif + diff --git a/packages/CLPBN/clpbn/bp/Solver.h b/packages/CLPBN/clpbn/bp/Solver.h new file mode 100644 index 000000000..483986278 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/Solver.h @@ -0,0 +1,50 @@ +#ifndef BP_SOLVER_H +#define BP_SOLVER_H + +#include + +#include "GraphicalModel.h" +#include "Variable.h" + +using namespace std; + +class Solver +{ + public: + Solver (const GraphicalModel* gm) + { + gm_ = gm; + } + virtual void runSolver (void) = 0; + virtual ParamSet getPosterioriOf (const Variable*) const = 0; + + void printPosterioriOf (const Variable* var) const + { + cout << endl; + cout << setw (20) << left << var->getLabel() << "posteriori" ; + cout << endl; + cout << "------------------------------" ; + cout << endl; + const Domain& domain = var->getDomain(); + ParamSet results = getPosterioriOf (var); + for (int xi = 0; xi < var->getDomainSize(); xi++) { + cout << setw (20) << domain[xi]; + cout << setprecision (PRECISION) << results[xi]; + cout << endl; + } + cout << endl; + } + + void printAllPosterioris (void) const + { + VarSet vars = gm_->getVariables(); + for (unsigned i = 0; i < vars.size(); i++) { + printPosterioriOf (vars[i]); + } + } + + private: + const GraphicalModel* gm_; +}; + +#endif diff --git a/packages/CLPBN/clpbn/bp/Variable.h b/packages/CLPBN/clpbn/bp/Variable.h new file mode 100755 index 000000000..63f3edcbc --- /dev/null +++ b/packages/CLPBN/clpbn/bp/Variable.h @@ -0,0 +1,143 @@ +#ifndef BP_GENERIC_VARIABLE_H +#define BP_GENERIC_VARIABLE_H + +#include + +#include +#include "Shared.h" + +using namespace std; + +class Variable +{ + public: + + Variable (unsigned varId) + { + this->varId_ = varId; + this->dsize_ = 0; + this->evidence_ = -1; + this->label_ = 0; + } + + Variable (unsigned varId, unsigned dsize, int evidence = -1) + { + assert (dsize != 0); + assert (evidence < (int)dsize); + this->varId_ = varId; + this->dsize_ = dsize; + this->evidence_ = evidence; + this->label_ = 0; + } + + Variable (unsigned varId, const Domain& domain, int evidence = -1) + { + assert (!domain.empty()); + assert (evidence < (int)domain.size()); + this->varId_ = varId; + this->dsize_ = domain.size(); + this->domain_ = domain; + this->evidence_ = evidence; + this->label_ = 0; + } + + ~Variable (void) + { + delete label_; + } + + unsigned getVarId (void) const { return varId_; } + unsigned getIndex (void) const { return index_; } + void setIndex (unsigned idx) { index_ = idx; } + int getDomainSize (void) const { return dsize_; } + bool hasEvidence (void) const { return evidence_ != -1; } + int getEvidence (void) const { return evidence_; } + bool hasDomain (void) { return !domain_.empty(); } + bool hasLabel (void) { return label_ != 0; } + + bool isValidStateIndex (int index) + { + return index >= 0 && index < dsize_; + } + + bool isValidState (const string& state) + { + return find (domain_.begin(), domain_.end(), state) != domain_.end(); + } + + Domain getDomain (void) const + { + assert (dsize_ != 0); + if (domain_.size() == 0) { + Domain d; + for (int i = 0; i < dsize_; i++) { + stringstream ss; + ss << "x" << i ; + d.push_back (ss.str()); + } + return d; + } else { + return domain_; + } + } + + void setDomainSize (unsigned dsize) + { + assert (dsize != 0); + dsize_ = dsize; + } + + void setDomain (const Domain& domain) + { + assert (!domain.empty()); + domain_ = domain; + dsize_ = domain.size(); + } + + void setEvidence (int ev) + { + assert (ev < dsize_); + evidence_ = ev; + } + + void setEvidence (const string& ev) + { + assert (isValidState (ev)); + for (unsigned i = 0; i < domain_.size(); i++) { + if (domain_[i] == ev) { + evidence_ = i; + } + } + } + + void setLabel (string label) + { + label_ = new string (label); + } + + string getLabel (void) const + { + if (label_ == 0) { + stringstream ss; + ss << "v" << varId_; + return ss.str(); + } else { + return *label_; + } + } + + protected: + unsigned varId_; + string* label_; + unsigned index_; + int evidence_; + + private: + DISALLOW_COPY_AND_ASSIGN (Variable); + Domain domain_; + int dsize_; + +}; + +#endif // BP_GENERIC_VARIABLE_H + diff --git a/packages/CLPBN/clpbn/bp/callgrind.h b/packages/CLPBN/clpbn/bp/callgrind.h new file mode 100755 index 000000000..d36b6f4eb --- /dev/null +++ b/packages/CLPBN/clpbn/bp/callgrind.h @@ -0,0 +1,147 @@ + +/* + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (callgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of callgrind, a valgrind tool for cache simulation + and call tree tracing. + + Copyright (C) 2003-2010 Josef Weidendorfer. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (callgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + +#ifndef __CALLGRIND_H +#define __CALLGRIND_H + +#include "valgrind.h" + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. + + The identification ('C','T') for Callgrind has historical + reasons: it was called "Calltree" before. Besides, ('C','G') would + clash with cachegrind. + */ + +typedef + enum { + VG_USERREQ__DUMP_STATS = VG_USERREQ_TOOL_BASE('C','T'), + VG_USERREQ__ZERO_STATS, + VG_USERREQ__TOGGLE_COLLECT, + VG_USERREQ__DUMP_STATS_AT, + VG_USERREQ__START_INSTRUMENTATION, + VG_USERREQ__STOP_INSTRUMENTATION + } Vg_CallgrindClientRequest; + +/* Dump current state of cost centers, and zero them afterwards */ +#define CALLGRIND_DUMP_STATS \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DUMP_STATS, \ + 0, 0, 0, 0, 0); \ + } + +/* Dump current state of cost centers, and zero them afterwards. + The argument is appended to a string stating the reason which triggered + the dump. This string is written as a description field into the + profile data dump. */ +#define CALLGRIND_DUMP_STATS_AT(pos_str) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DUMP_STATS_AT, \ + pos_str, 0, 0, 0, 0); \ + } + +/* Zero cost centers */ +#define CALLGRIND_ZERO_STATS \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__ZERO_STATS, \ + 0, 0, 0, 0, 0); \ + } + +/* Toggles collection state. + The collection state specifies whether the happening of events + should be noted or if they are to be ignored. Events are noted + by increment of counters in a cost center */ +#define CALLGRIND_TOGGLE_COLLECT \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__TOGGLE_COLLECT, \ + 0, 0, 0, 0, 0); \ + } + +/* Start full callgrind instrumentation if not already switched on. + When cache simulation is done, it will flush the simulated cache; + this will lead to an artifical cache warmup phase afterwards with + cache misses which would not have happened in reality. */ +#define CALLGRIND_START_INSTRUMENTATION \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__START_INSTRUMENTATION, \ + 0, 0, 0, 0, 0); \ + } + +/* Stop full callgrind instrumentation if not already switched off. + This flushes Valgrinds translation cache, and does no additional + instrumentation afterwards, which effectivly will run at the same + speed as the "none" tool (ie. at minimal slowdown). + Use this to bypass Callgrind aggregation for uninteresting code parts. + To start Callgrind in this mode to ignore the setup phase, use + the option "--instr-atstart=no". */ +#define CALLGRIND_STOP_INSTRUMENTATION \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STOP_INSTRUMENTATION, \ + 0, 0, 0, 0, 0); \ + } + +#endif /* __CALLGRIND_H */ diff --git a/packages/CLPBN/clpbn/bp/examples/bayes-ball a.xml b/packages/CLPBN/clpbn/bp/examples/bayes-ball a.xml new file mode 100755 index 000000000..286b95f59 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/bayes-ball a.xml @@ -0,0 +1,76 @@ + + + + +Bayes-Ball: The Rational Pastime Network, Figure 4, a) + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 1 + 1
+
+ + + 2 + 1 + 3 + 1
+
+ + + 3 + 1
+
+ + + 4 + 1 + 5 + 1
+
+ + + 5 + 2 + 6 + 1
+
+ + + 6 + 3 + 1
+
+ +
+
+ diff --git a/packages/CLPBN/clpbn/bp/examples/bayes-ball c.xml b/packages/CLPBN/clpbn/bp/examples/bayes-ball c.xml new file mode 100755 index 000000000..44b291822 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/bayes-ball c.xml @@ -0,0 +1,74 @@ + + + + +Bayes-Ball: The Rational Pastime Network, Figure 4, c) + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 1 + 1
+
+ + + 2 + 1 + 3 + 1
+
+ + + 3 + 1
+
+ + + 4 + 5 + 1
+
+ + + 5 + 2 + 6 + 1
+
+ + + 6 + 1
+
+ +
+
+ diff --git a/packages/CLPBN/clpbn/bp/examples/burglary-alarm.uai b/packages/CLPBN/clpbn/bp/examples/burglary-alarm.uai new file mode 100755 index 000000000..3f24754e2 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/burglary-alarm.uai @@ -0,0 +1,28 @@ +MARKOV +5 +2 2 2 2 2 +5 +1 0 +1 1 +3 2 0 1 +2 3 2 +2 4 2 + +2 + .001 .009 + +2 + .002 .008 + +8 + .95 .94 .29 .001 + .05 .06 .71 .999 + +4 + .9 .05 + .1 .95 + +4 + .7 .01 + .3 .99 + diff --git a/packages/CLPBN/clpbn/bp/examples/burglary-alarm.xml b/packages/CLPBN/clpbn/bp/examples/burglary-alarm.xml new file mode 100755 index 000000000..36c7a500b --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/burglary-alarm.xml @@ -0,0 +1,81 @@ + + + + + + + +Simple Loop + + + B + b1 + b2 + + + + E + e1 + e2 + + + + A + a1 + a2 + + + + J + j1 + j2 + + + + M + m1 + m2 + + + + B + .001 .009
+
+ + + E + .002 .008
+
+ + + A + B + E + .95 .05 .94 .06 .29 .71 .001 .999
+
+ + + J + A + .9 .1 .05 .95
+
+ + + M + A + .7 .3 .01 .99
+
+ +
+
+ diff --git a/packages/CLPBN/clpbn/bp/examples/burglary-alarm.yap b/packages/CLPBN/clpbn/bp/examples/burglary-alarm.yap new file mode 100755 index 000000000..9fd004712 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/burglary-alarm.yap @@ -0,0 +1,54 @@ + +:- use_module(library(clpbn)). + +:- set_clpbn_flag(solver, vel). + +% +% B E +% \ / +% \ / +% A +% / \ +% / \ +% J M +% + + +b(B) :- + b_table(BDist), + { B = b with p([b1, b2], BDist) }. + +e(E) :- + e_table(EDist), + { E = e with p([e1, e2], EDist) }. + +a(A) :- + b(B), + e(E), + a_table(ADist), + { A = a with p([a1, a2], ADist, [B, E]) }. + +j(J):- + a(A), + j_table(JDist), + { J = j with p([j1, j2], JDist, [A]) }. + +m(M):- + a(A), + m_table(MDist), + { M = m with p([m1, m2], MDist, [A]) }. + + +b_table([0.001, 0.009]). + +e_table([0.002, 0.008]). + +a_table([0.95, 0.94, 0.29, 0.001, + 0.05, 0.06, 0.71, 0.999]). + +j_table([0.9, 0.05, + 0.1, 0.95]). + +m_table([0.7, 0.01, + 0.3, 0.99]). + diff --git a/packages/CLPBN/clpbn/bp/examples/chain.xml b/packages/CLPBN/clpbn/bp/examples/chain.xml new file mode 100755 index 000000000..6b5882e5d --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/chain.xml @@ -0,0 +1,58 @@ + + + + + + +Simple Chain + + + A + a1 + a2 + + + + B + b1 + b2 + + + + C + c1 + c2 + + + + A + 0.3 0.7
+
+ + + B + A + 0.4 0.6 0.2 0.8
+
+ + + C + B + 0.9 0.1 0.25 0.75
+
+ +
+
+ diff --git a/packages/CLPBN/clpbn/bp/examples/convergence.xml b/packages/CLPBN/clpbn/bp/examples/convergence.xml new file mode 100755 index 000000000..8172042ed --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/convergence.xml @@ -0,0 +1,51 @@ + + + + + + +Simple Convergence + + + A + + + + + B + + + + + C + + + + + A + 1
+
+ + + B + 1
+
+ + + C + A + B + 1
+
+ +
+
+ diff --git a/packages/CLPBN/clpbn/bp/examples/divergence.xml b/packages/CLPBN/clpbn/bp/examples/divergence.xml new file mode 100755 index 000000000..69ef00f04 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/divergence.xml @@ -0,0 +1,51 @@ + + + + + + +Simple Divergence + + + A + + + + + B + + + + + C + + + + + A + 1
+
+ + + B + A + 1
+
+ + + C + A + 1
+
+ +
+
+ diff --git a/packages/CLPBN/clpbn/bp/examples/john-mary-call.xml b/packages/CLPBN/clpbn/bp/examples/john-mary-call.xml new file mode 100755 index 000000000..037134fc5 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/john-mary-call.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + +]> + + + + +John-Mary-Call + + + + Burglary + discrete + False + True + position = (145, 114) + + + + Earthquake + discrete + False + True + position = (351, 110) + + + + Alarm + discrete + False + True + position = (253, 224) + + + + JohnCalls + discrete + False + True + position = (156, 343) + + + + MaryCalls + discrete + False + True + position = (344, 341) + + + + + + Burglary + 0.999 0.0010
+
+ + + Earthquake + 0.998 0.0020
+
+ + + Alarm + Burglary + Earthquake + 0.999 0.71 0.06 0.05 0.0010 0.29 0.94 0.95
+
+ + + JohnCalls + Alarm + 0.95 0.1 0.05 0.9
+
+ + + MaryCalls + Alarm + 0.99 0.3 0.01 0.7
+
+ + +
diff --git a/packages/CLPBN/clpbn/bp/examples/loop.xml b/packages/CLPBN/clpbn/bp/examples/loop.xml new file mode 100755 index 000000000..2764d73d1 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/loop.xml @@ -0,0 +1,81 @@ + + + + + + +Loop + + + A + a1 + a2 + + + + B + b1 + b2 + + + + C + c1 + c2 + + + + D + d1 + d2 + + + + E + e1 + e2 + + + + A + .01 .09
+
+ + + B + A + .03 .97 .6 .4
+
+ + + C + A + E + .24 .76 .12 .88 .2 .4. 5. .6
+
+ + + D + B + C + .2 .8 .7 .3 .45 .55 .22 .78
+
+ + + E + .5 .6
+
+ +
+
+ diff --git a/packages/CLPBN/clpbn/bp/examples/loop.yap b/packages/CLPBN/clpbn/bp/examples/loop.yap new file mode 100755 index 000000000..c18784975 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/loop.yap @@ -0,0 +1,53 @@ + +:- use_module(library(clpbn)). + +:- set_clpbn_flag(solver, bp). + +% +% A E +% / \ / +% / \ / +% B C +% \ / +% \ / +% D +% + +a(A) :- + a_table(ADist), + { A = a with p([a1, a2], ADist) }. + +b(B) :- + a(A), + b_table(BDist), + { B = b with p([b1, b2], BDist, [A]) }. + +c(C) :- + a(A), + c_table(CDist), + { C = c with p([c1, c2], CDist, [A]) }. + +d(D) :- + b(B), + c(C), + d_table(DDist), + { D = d with p([d1, d2], DDist, [B, C]) }. + +e(E) :- + e_table(EDist), + { E = e with p([e1, e2], EDist) }. + + +a_table([0.005, 0.995]). + +b_table([0.02, 0.97, + 0.88, 0.03]). + +c_table([0.55, 0.94, + 0.45, 0.06]). + +d_table([0.192, 0.98, 0.33, 0.013, + 0.908, 0.02, 0.77, 0.987]). + +e_table([0.055, 0.945]). + diff --git a/packages/CLPBN/clpbn/bp/examples/neapolitan-A-F-joint.yap b/packages/CLPBN/clpbn/bp/examples/neapolitan-A-F-joint.yap new file mode 100755 index 000000000..4cb95eab4 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/neapolitan-A-F-joint.yap @@ -0,0 +1,55 @@ + +:- use_module(library(clpbn)). + +:- set_clpbn_flag(solver, bp). + +% +% B F +% \ / +% \ / +% A +% + +b(B) :- + b_table(BDist), + { B = b with p([b1, b2], BDist) }. + +f(F) :- + f_table(FDist), + { F = f with p([f1, f2], FDist) }. + +a(A) :- + b(B), + f(F), + a_table(ADist), + { A = a with p([a1, a2], ADist, [B, F]) }. + +d(D) :- + a(A), + f(F), + d_table(DDist), + { D = d with p([d1, d2, d3, d4], DDist, [A, F]) }. + + +b_table([0.005, 0.995]). + +f_table([0.03, 0.97]). + +a_table([0.992, 0.99, 0.2, 0.003, + 0.008, 0.01, 0.8, 0.997]). + +d_table([1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0]). + +%d_table([0.997, 0.001, 0.001, 0.001, +% 0.001, 0.997, 0.001, 0.001, +% 0.001, 0.001, 0.997, 0.001, +% 0.001, 0.001, 0.001, 0.997]). + +%d_table([0.15, 0.1, 0.7, 0.5, +% 0.25, 0.3, 0.2, 0.25, +% 0.3, 0.15, 0.35, 0.2, +% 0.3, 0.4, 0.2, 0.1]). + diff --git a/packages/CLPBN/clpbn/bp/examples/neapolitan.uai b/packages/CLPBN/clpbn/bp/examples/neapolitan.uai new file mode 100755 index 000000000..1339c83c2 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/neapolitan.uai @@ -0,0 +1,17 @@ +MARKOV +3 +2 2 2 +3 +1 0 +1 1 +3 2 0 1 + +2 + 0.005 0.995 + +2 + 0.03 0.97 + +8 + 0.992 0.99 0.2 0.003 + 0.008 0.01 0.8 0.997 diff --git a/packages/CLPBN/clpbn/bp/examples/neapolitan.xml b/packages/CLPBN/clpbn/bp/examples/neapolitan.xml new file mode 100755 index 000000000..0b359e274 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/neapolitan.xml @@ -0,0 +1,53 @@ + + + + + + +Neapolitan + + + B + b1 + b2 + + + + F + f1 + f2 + + + + A + a1 + a2 + + + + B + .005 .995
+
+ + + F + .03 .97
+
+ + + A + B + F + .992 .008 .99 .01 .2 .8 .003 .997
+
+ +
+
+ diff --git a/packages/CLPBN/clpbn/bp/examples/neapolitan.yap b/packages/CLPBN/clpbn/bp/examples/neapolitan.yap new file mode 100755 index 000000000..1d558d634 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/neapolitan.yap @@ -0,0 +1,35 @@ + +:- use_module(library(clpbn)). + +:- set_clpbn_flag(solver, bp). + +% +% B F +% \ / +% \ / +% A +% + + +b(B) :- + b_table(BDist), + { B = b with p([b1, b2], BDist) }. + +f(F) :- + f_table(FDist), + { F = f with p([f1, f2], FDist) }. + +a(A) :- + b(B), + f(F), + a_table(ADist), + { A = a with p([a1, a2], ADist, [B, F]) }. + + +b_table([0.005, 0.995]). + +f_table([0.03, 0.97]). + +a_table([0.992, 0.99, 0.2, 0.003, + 0.008, 0.01, 0.8, 0.997]). + diff --git a/packages/CLPBN/clpbn/bp/examples/several-parents-and-childs.xml b/packages/CLPBN/clpbn/bp/examples/several-parents-and-childs.xml new file mode 100755 index 000000000..e37c7ac5e --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/several-parents-and-childs.xml @@ -0,0 +1,128 @@ + + + + + + +Node with several parents and childs + + + A + a1 + a2 + + + + B + b1 + b2 + b3 + b4 + + + + C + c1 + c2 + c3 + + + + D + d1 + d2 + d3 + + + + E + e1 + e2 + e3 + e4 + + + + F + f1 + f2 + f3 + + + + G + g1 + g2 + + + + + A + .1 .2
+
+ + + B + .01 .02 .03 .04
+
+ + + C + .11 .22 .33
+
+ + + D + A + B + C + + .522 .008 .99 .01 .2 .8 .003 .457 .423 .007 .92 .04 .5 .232 .033 .227 .112 .048 .91 .21 .24 .18 .005 .227 + .212 .04 .59 .21 .6 .1 .023 .215 .913 .017 .96 .01 .55 .422 .013 .417 .272 .068 .61 .11 .26 .28 .205 .322 + .142 .028 .19 .11 .5 .67 .013 .437 .163 .067 .12 .06 .1 .262 .063 .167 .512 .028 .11 .41 .14 .68 .015 .92 +
+
+ + + E + D + + .111 .11 .1 + .222 .22 .2 + .333 .33 .3 + .444 .44 .4 +
+
+ + + F + D + + .112 .111 .110 + .223 .222 .221 + .334 .333 .332 +
+
+ + + G + D + + .101 .102 .103 + .201 .202 .203 +
+
+ +
+
+ diff --git a/packages/CLPBN/clpbn/bp/examples/test.uai b/packages/CLPBN/clpbn/bp/examples/test.uai new file mode 100755 index 000000000..f773cd9bf --- /dev/null +++ b/packages/CLPBN/clpbn/bp/examples/test.uai @@ -0,0 +1,36 @@ +MARKOV +5 +4 2 3 2 3 +7 +1 0 +1 1 +1 2 +1 3 +1 4 +2 0 1 +4 1 2 3 4 + +4 + 0.1 0.7 0.43 0.22 + +2 + 0.2 0.6 + +3 + 0.3 0.5 0.2 + +2 + 0.15 0.75 + +3 + 0.25 0.45 0.15 + +8 + 0.210 0.333 0.457 0.4 + 0.811 0.000 0.189 0.89 + +36 + 0.1 0.15 0.2 0.25 0.3 0.45 0.5 0.55 0.65 0.7 0.75 0.9 + 0.11 0.22 0.33 0.44 0.55 0.66 0.77 0.88 0.91 0.93 0.95 0.97 + 0.42 0.22 0.33 0.44 0.15 0.36 0.27 0.28 0.21 0.13 0.25 0.17 + diff --git a/packages/CLPBN/clpbn/bp/valgrind.h b/packages/CLPBN/clpbn/bp/valgrind.h new file mode 100755 index 000000000..0f5b37662 --- /dev/null +++ b/packages/CLPBN/clpbn/bp/valgrind.h @@ -0,0 +1,4536 @@ +/* -*- c -*- + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (valgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2010 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (valgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query Valgrind's + execution inside your own programs. + + The resulting executables will still run without Valgrind, just a + little bit more slowly than they otherwise would, but otherwise + unchanged. When not running on valgrind, each client request + consumes very few (eg. 7) instructions, so the resulting performance + loss is negligible unless you plan to execute client requests + millions of times per second. Nevertheless, if that is still a + problem, you can compile with the NVALGRIND symbol defined (gcc + -DNVALGRIND) so that client requests are not even compiled in. */ + +#ifndef __VALGRIND_H +#define __VALGRIND_H + + +/* ------------------------------------------------------------------ */ +/* VERSION NUMBER OF VALGRIND */ +/* ------------------------------------------------------------------ */ + +/* Specify Valgrind's version number, so that user code can + conditionally compile based on our version number. Note that these + were introduced at version 3.6 and so do not exist in version 3.5 + or earlier. The recommended way to use them to check for "version + X.Y or later" is (eg) + +#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \ + && (__VALGRIND_MAJOR__ > 3 \ + || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) +*/ +#define __VALGRIND_MAJOR__ 3 +#define __VALGRIND_MINOR__ 6 + + +#include + +/* Nb: this file might be included in a file compiled with -ansi. So + we can't use C++ style "//" comments nor the "asm" keyword (instead + use "__asm__"). */ + +/* Derive some tags indicating what the target platform is. Note + that in this file we're using the compiler's CPP symbols for + identifying architectures, which are different to the ones we use + within the rest of Valgrind. Note, __powerpc__ is active for both + 32 and 64-bit PPC, whereas __powerpc64__ is only active for the + latter (on Linux, that is). + + Misc note: how to find out what's predefined in gcc by default: + gcc -Wp,-dM somefile.c +*/ +#undef PLAT_ppc64_aix5 +#undef PLAT_ppc32_aix5 +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64_linux +#undef PLAT_arm_linux + +#if defined(_AIX) && defined(__64BIT__) +# define PLAT_ppc64_aix5 1 +#elif defined(_AIX) && !defined(__64BIT__) +# define PLAT_ppc32_aix5 1 +#elif defined(__APPLE__) && defined(__i386__) +# define PLAT_x86_darwin 1 +#elif defined(__APPLE__) && defined(__x86_64__) +# define PLAT_amd64_darwin 1 +#elif defined(__linux__) && defined(__i386__) +# define PLAT_x86_linux 1 +#elif defined(__linux__) && defined(__x86_64__) +# define PLAT_amd64_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) +# define PLAT_ppc32_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) +# define PLAT_ppc64_linux 1 +#elif defined(__linux__) && defined(__arm__) +# define PLAT_arm_linux 1 +#else +/* If we're not compiling for our target platform, don't generate + any inline asms. */ +# if !defined(NVALGRIND) +# define NVALGRIND 1 +# endif +#endif + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ +/* in here of use to end-users -- skip to the next section. */ +/* ------------------------------------------------------------------ */ + +#if defined(NVALGRIND) + +/* Define NVALGRIND to completely remove the Valgrind magic sequence + from the compiled code (analogous to NDEBUG's effects on + assert()) */ +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { \ + (_zzq_rlval) = (_zzq_default); \ + } + +#else /* ! NVALGRIND */ + +/* The following defines the magic code sequences which the JITter + spots and handles magically. Don't look too closely at them as + they will rot your brain. + + The assembly code sequences for all architectures is in this one + file. This is because this file must be stand-alone, and we don't + want to have multiple files. + + For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default + value gets put in the return slot, so that everything works when + this is executed not under Valgrind. Args are passed in a memory + block, and so there's no intrinsic limit to the number that could + be passed, but it's currently five. + + The macro args are: + _zzq_rlval result lvalue + _zzq_default default value (result returned when running on real CPU) + _zzq_request request code + _zzq_arg1..5 request params + + The other two macros are used to support function wrapping, and are + a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the + guest's NRADDR pseudo-register and whatever other information is + needed to safely run the call original from the wrapper: on + ppc64-linux, the R2 value at the divert point is also needed. This + information is abstracted into a user-visible type, OrigFn. + + VALGRIND_CALL_NOREDIR_* behaves the same as the following on the + guest, but guarantees that the branch instruction will not be + redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: + branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a + complete inline asm, since it needs to be combined with more magic + inline asm stuff to be useful. +*/ + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "roll $3, %%edi ; roll $13, %%edi\n\t" \ + "roll $29, %%edi ; roll $19, %%edi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EDX = client_request ( %EAX ) */ \ + "xchgl %%ebx,%%ebx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + "xchgl %%ecx,%%ecx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%EAX */ \ + "xchgl %%edx,%%edx\n\t" +#endif /* PLAT_x86_linux || PLAT_x86_darwin */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ + "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RDX = client_request ( %RAX ) */ \ + "xchgq %%rbx,%%rbx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RAX = guest_NRADDR */ \ + "xchgq %%rcx,%%rcx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_RAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%RAX */ \ + "xchgq %%rdx,%%rdx\n\t" +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \ + "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned int _zzq_args[6]; \ + unsigned int _zzq_result; \ + unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned long long int _zzq_args[6]; \ + register unsigned long long int _zzq_result __asm__("r3"); \ + register unsigned long long int* _zzq_ptr __asm__("r4"); \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1" \ + : "=r" (_zzq_result) \ + : "0" (_zzq_default), "r" (_zzq_ptr) \ + : "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned long long int __addr __asm__("r3"); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2" \ + : "=r" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4" \ + : "=r" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc64_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ + "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("mov r3, %1\n\t" /*default*/ \ + "mov r4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = client_request ( R4 ) */ \ + "orr r10, r10, r10\n\t" \ + "mov %0, r3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "r3", "r4"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = guest_NRADDR */ \ + "orr r11, r11, r11\n\t" \ + "mov %0, r3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R4 */ \ + "orr r12, r12, r12\n\t" + +#endif /* PLAT_arm_linux */ + +/* ------------------------ ppc32-aix5 ------------------------- */ + +#if defined(PLAT_ppc32_aix5) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + unsigned int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \ + "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned int _zzq_args[7]; \ + register unsigned int _zzq_result; \ + register unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_args[6] = (unsigned int)(_zzq_default); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 4,%1\n\t" \ + "lwz 3, 24(4)\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" \ + : "=b" (_zzq_result) \ + : "b" (_zzq_ptr) \ + : "r3", "r4", "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc32_aix5 */ + +/* ------------------------ ppc64-aix5 ------------------------- */ + +#if defined(PLAT_ppc64_aix5) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned long long int _zzq_args[7]; \ + register unsigned long long int _zzq_result; \ + register unsigned long long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int long long)(_zzq_request); \ + _zzq_args[1] = (unsigned int long long)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int long long)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int long long)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int long long)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int long long)(_zzq_arg5); \ + _zzq_args[6] = (unsigned int long long)(_zzq_default); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 4,%1\n\t" \ + "ld 3, 48(4)\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" \ + : "=b" (_zzq_result) \ + : "b" (_zzq_ptr) \ + : "r3", "r4", "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc64_aix5 */ + +/* Insert assembly code for other platforms here... */ + +#endif /* NVALGRIND */ + + +/* ------------------------------------------------------------------ */ +/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ +/* ugly. It's the least-worst tradeoff I can think of. */ +/* ------------------------------------------------------------------ */ + +/* This section defines magic (a.k.a appalling-hack) macros for doing + guaranteed-no-redirection macros, so as to get from function + wrappers to the functions they are wrapping. The whole point is to + construct standard call sequences, but to do the call itself with a + special no-redirect call pseudo-instruction that the JIT + understands and handles specially. This section is long and + repetitious, and I can't see a way to make it shorter. + + The naming scheme is as follows: + + CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} + + 'W' stands for "word" and 'v' for "void". Hence there are + different macros for calling arity 0, 1, 2, 3, 4, etc, functions, + and for each, the possibility of returning a word-typed result, or + no result. +*/ + +/* Use these to write the name of your wrapper. NOTE: duplicates + VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. */ + +/* Use an extra level of macroisation so as to ensure the soname/fnname + args are fully macro-expanded before pasting them together. */ +#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd + +#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgwZU_,soname,_,fnname) + +#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgwZZ_,soname,_,fnname) + +/* Use this macro from within a wrapper function to collect the + context (address and possibly other info) of the original function. + Once you have that you can then use it in one of the CALL_FN_ + macros. The type of the argument _lval is OrigFn. */ +#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) + +/* Derivatives of the main macros below, for calling functions + returning void. */ + +#define CALL_FN_v_v(fnptr) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_v(_junk,fnptr); } while (0) + +#define CALL_FN_v_W(fnptr, arg1) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_W(_junk,fnptr,arg1); } while (0) + +#define CALL_FN_v_WW(fnptr, arg1,arg2) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) + +#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) + +#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) + +#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) + +#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) + +#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) + +/* These regs are trashed by the hidden call. No need to mention eax + as gcc can already see that, plus causes gcc to bomb. */ +#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" + +/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $4, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $8, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $12, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $16, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $20, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $24, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $28, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $32, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $36, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $40, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $44, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "pushl 48(%%eax)\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $48, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) + +/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ + "rdi", "r8", "r9", "r10", "r11" + +/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned + long) == 8. */ + +/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ + macros. In order not to trash the stack redzone, we need to drop + %rsp by 128 before the hidden call, and restore afterwards. The + nastyness is that it is only by luck that the stack still appears + to be unwindable during the hidden call - since then the behaviour + of any routine using this macro does not match what the CFI data + says. Sigh. + + Why is this important? Imagine that a wrapper has a stack + allocated local, and passes to the hidden call, a pointer to it. + Because gcc does not know about the hidden call, it may allocate + that local in the redzone. Unfortunately the hidden call may then + trash it before it comes to use it. So we must step clear of the + redzone, for the duration of the hidden call, to make it safe. + + Probably the same problem afflicts the other redzone-style ABIs too + (ppc64-linux, ppc32-aix5, ppc64-aix5); but for those, the stack is + self describing (none of this CFI nonsense) so at least messing + with the stack pointer doesn't give a danger of non-unwindable + stack. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CALL_NOREDIR_RAX \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $8, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $16, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $24, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $32, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $40, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "subq $128,%%rsp\n\t" \ + "pushq 96(%%rax)\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $48, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +/* This is useful for finding out about the on-stack stuff: + + extern int f9 ( int,int,int,int,int,int,int,int,int ); + extern int f10 ( int,int,int,int,int,int,int,int,int,int ); + extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); + extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); + + int g9 ( void ) { + return f9(11,22,33,44,55,66,77,88,99); + } + int g10 ( void ) { + return f10(11,22,33,44,55,66,77,88,99,110); + } + int g11 ( void ) { + return f11(11,22,33,44,55,66,77,88,99,110,121); + } + int g12 ( void ) { + return f12(11,22,33,44,55,66,77,88,99,110,121,132); + } +*/ + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* These CALL_FN_ macros assume that on ppc32-linux, + sizeof(unsigned long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,16\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,16\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,32\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,20(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,32\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,128" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,128" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,144" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,144" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14" + +/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "ldr r0, [%1, #20] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #4 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #8 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #12 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "push {r0, r1, r2, r3} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #16 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #20 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "ldr r0, [%1, #40] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #24 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #28 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory",__CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "ldr r2, [%1, #48] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #32 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ ppc32-aix5 ------------------------- */ + +#if defined(PLAT_ppc32_aix5) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Expand the stack frame, copying enough info that unwinding + still works. Trashes r3. */ + +#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \ + "addi 1,1,-" #_n_fr "\n\t" \ + "lwz 3," #_n_fr "(1)\n\t" \ + "stw 3,0(1)\n\t" + +#define VG_CONTRACT_FRAME_BY(_n_fr) \ + "addi 1,1," #_n_fr "\n\t" + +/* These CALL_FN_ macros assume that on ppc32-aix5, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(64) \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(64) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(64) \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(64) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(72) \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,64(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(72) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(72) \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,68(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,64(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(72) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_aix5 */ + +/* ------------------------ ppc64-aix5 ------------------------- */ + +#if defined(PLAT_ppc64_aix5) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Expand the stack frame, copying enough info that unwinding + still works. Trashes r3. */ + +#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \ + "addi 1,1,-" #_n_fr "\n\t" \ + "ld 3," #_n_fr "(1)\n\t" \ + "std 3,0(1)\n\t" + +#define VG_CONTRACT_FRAME_BY(_n_fr) \ + "addi 1,1," #_n_fr "\n\t" + +/* These CALL_FN_ macros assume that on ppc64-aix5, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(128) \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(128) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(128) \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(128) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(144) \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(144) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(144) \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(144) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64_aix5 */ + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ +/* */ +/* ------------------------------------------------------------------ */ + +/* Some request codes. There are many more of these, but most are not + exposed to end-user view. These are the public ones, all of the + form 0x1000 + small_number. + + Core ones are in the range 0x00000000--0x0000ffff. The non-public + ones start at 0x2000. +*/ + +/* These macros are used by tools -- they must be public, but don't + embed them into other programs. */ +#define VG_USERREQ_TOOL_BASE(a,b) \ + ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) +#define VG_IS_TOOL_USERREQ(a, b, v) \ + (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ +typedef + enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, + VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, + + /* These allow any function to be called from the simulated + CPU but run on the real CPU. Nb: the first arg passed to + the function is always the ThreadId of the running + thread! So CLIENT_CALL0 actually requires a 1 arg + function, etc. */ + VG_USERREQ__CLIENT_CALL0 = 0x1101, + VG_USERREQ__CLIENT_CALL1 = 0x1102, + VG_USERREQ__CLIENT_CALL2 = 0x1103, + VG_USERREQ__CLIENT_CALL3 = 0x1104, + + /* Can be useful in regression testing suites -- eg. can + send Valgrind's output to /dev/null and still count + errors. */ + VG_USERREQ__COUNT_ERRORS = 0x1201, + + /* These are useful and can be interpreted by any tool that + tracks malloc() et al, by using vg_replace_malloc.c. */ + VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, + VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, + VG_USERREQ__MEMPOOL_TRIM = 0x1307, + VG_USERREQ__MOVE_MEMPOOL = 0x1308, + VG_USERREQ__MEMPOOL_CHANGE = 0x1309, + VG_USERREQ__MEMPOOL_EXISTS = 0x130a, + + /* Allow printfs to valgrind log. */ + /* The first two pass the va_list argument by value, which + assumes it is the same size as or smaller than a UWord, + which generally isn't the case. Hence are deprecated. + The second two pass the vargs by reference and so are + immune to this problem. */ + /* both :: char* fmt, va_list vargs (DEPRECATED) */ + VG_USERREQ__PRINTF = 0x1401, + VG_USERREQ__PRINTF_BACKTRACE = 0x1402, + /* both :: char* fmt, va_list* vargs */ + VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, + + /* Stack support. */ + VG_USERREQ__STACK_REGISTER = 0x1501, + VG_USERREQ__STACK_DEREGISTER = 0x1502, + VG_USERREQ__STACK_CHANGE = 0x1503, + + /* Wine support */ + VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601 + } Vg_ClientRequest; + +#if !defined(__GNUC__) +# define __extension__ /* */ +#endif + +/* Returns the number of Valgrinds this code is running under. That + is, 0 if running natively, 1 if running under Valgrind, 2 if + running under Valgrind which is running under another Valgrind, + etc. */ +#define RUNNING_ON_VALGRIND __extension__ \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0 /* if not */, \ + VG_USERREQ__RUNNING_ON_VALGRIND, \ + 0, 0, 0, 0, 0); \ + _qzz_res; \ + }) + + +/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + + _qzz_len - 1]. Useful if you are debugging a JITter or some such, + since it provides a way to make sure valgrind will retranslate the + invalidated area. Returns no value. */ +#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DISCARD_TRANSLATIONS, \ + _qzz_addr, _qzz_len, 0, 0, 0); \ + } + + +/* These requests are for getting Valgrind itself to print something. + Possibly with a backtrace. This is a really ugly hack. The return value + is the number of characters printed, excluding the "**** " part at the + start and the backtrace (if present). */ + +#if defined(NVALGRIND) + +# define VALGRIND_PRINTF(...) +# define VALGRIND_PRINTF_BACKTRACE(...) + +#else /* NVALGRIND */ + +/* Modern GCC will optimize the static routine out if unused, + and unused attribute will shut down warnings about it. */ +static int VALGRIND_PRINTF(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +static int +VALGRIND_PRINTF(const char *format, ...) +{ + unsigned long _qzz_res; + va_list vargs; + va_start(vargs, format); + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); + va_end(vargs); + return (int)_qzz_res; +} + +static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +static int +VALGRIND_PRINTF_BACKTRACE(const char *format, ...) +{ + unsigned long _qzz_res; + va_list vargs; + va_start(vargs, format); + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); + va_end(vargs); + return (int)_qzz_res; +} + +#endif /* NVALGRIND */ + + +/* These requests allow control to move from the simulated CPU to the + real CPU, calling an arbitary function. + + Note that the current ThreadId is inserted as the first argument. + So this call: + + VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) + + requires f to have this signature: + + Word f(Word tid, Word arg1, Word arg2) + + where "Word" is a word-sized type. + + Note that these client requests are not entirely reliable. For example, + if you call a function with them that subsequently calls printf(), + there's a high chance Valgrind will crash. Generally, your prospects of + these working are made higher if the called function does not refer to + any global variables, and does not refer to any libc or other functions + (printf et al). Any kind of entanglement with libc or dynamic linking is + likely to have a bad outcome, for tricky reasons which we've grappled + with a lot in the past. +*/ +#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL0, \ + _qyy_fn, \ + 0, 0, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL1, \ + _qyy_fn, \ + _qyy_arg1, 0, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL2, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL3, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, \ + _qyy_arg3, 0); \ + _qyy_res; \ + }) + + +/* Counts the number of errors that have been recorded by a tool. Nb: + the tool must record the errors with VG_(maybe_record_error)() or + VG_(unique_error)() for them to be counted. */ +#define VALGRIND_COUNT_ERRORS \ + __extension__ \ + ({unsigned int _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__COUNT_ERRORS, \ + 0, 0, 0, 0, 0); \ + _qyy_res; \ + }) + +/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing + when heap blocks are allocated in order to give accurate results. This + happens automatically for the standard allocator functions such as + malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, + delete[], etc. + + But if your program uses a custom allocator, this doesn't automatically + happen, and Valgrind will not do as well. For example, if you allocate + superblocks with mmap() and then allocates chunks of the superblocks, all + Valgrind's observations will be at the mmap() level and it won't know that + the chunks should be considered separate entities. In Memcheck's case, + that means you probably won't get heap block overrun detection (because + there won't be redzones marked as unaddressable) and you definitely won't + get any leak detection. + + The following client requests allow a custom allocator to be annotated so + that it can be handled accurately by Valgrind. + + VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated + by a malloc()-like function. For Memcheck (an illustrative case), this + does two things: + + - It records that the block has been allocated. This means any addresses + within the block mentioned in error messages will be + identified as belonging to the block. It also means that if the block + isn't freed it will be detected by the leak checker. + + - It marks the block as being addressable and undefined (if 'is_zeroed' is + not set), or addressable and defined (if 'is_zeroed' is set). This + controls how accesses to the block by the program are handled. + + 'addr' is the start of the usable block (ie. after any + redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator + can apply redzones -- these are blocks of padding at the start and end of + each block. Adding redzones is recommended as it makes it much more likely + Valgrind will spot block overruns. `is_zeroed' indicates if the memory is + zeroed (or filled with another predictable value), as is the case for + calloc(). + + VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a + heap block -- that will be used by the client program -- is allocated. + It's best to put it at the outermost level of the allocator if possible; + for example, if you have a function my_alloc() which calls + internal_alloc(), and the client request is put inside internal_alloc(), + stack traces relating to the heap block will contain entries for both + my_alloc() and internal_alloc(), which is probably not what you want. + + For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out + custom blocks from within a heap block, B, that has been allocated with + malloc/calloc/new/etc, then block B will be *ignored* during leak-checking + -- the custom blocks will take precedence. + + VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For + Memcheck, it does two things: + + - It records that the block has been deallocated. This assumes that the + block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - It marks the block as being unaddressable. + + VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a + heap block is deallocated. + + In many cases, these two client requests will not be enough to get your + allocator working well with Memcheck. More specifically, if your allocator + writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call + will be necessary to mark the memory as addressable just before the zeroing + occurs, otherwise you'll get a lot of invalid write errors. For example, + you'll need to do this if your allocator recycles freed blocks, but it + zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). + Alternatively, if your allocator reuses freed blocks for allocator-internal + data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. + + Really, what's happening is a blurring of the lines between the client + program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the + memory should be considered unaddressable to the client program, but the + allocator knows more than the rest of the client program and so may be able + to safely access it. Extra client requests are necessary for Valgrind to + understand the distinction between the allocator and the rest of the + program. + + Note: there is currently no VALGRIND_REALLOCLIKE_BLOCK client request; it + has to be emulated with MALLOCLIKE/FREELIKE and memory copying. + + Ignored if addr == 0. +*/ +#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed, 0); \ + } + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0, 0); \ + } + +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0, 0); \ + } + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0, 0); \ + } + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0, 0); \ + } + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0, 0); \ + } + +/* Disassociate any pieces outside a particular range. */ +#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_TRIM, \ + pool, addr, size, 0, 0); \ + } + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MOVE_MEMPOOL, \ + poolA, poolB, 0, 0, 0); \ + } + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_CHANGE, \ + pool, addrA, addrB, size, 0); \ + } + +/* Return 1 if a mempool exists, else 0. */ +#define VALGRIND_MEMPOOL_EXISTS(pool) \ + __extension__ \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_EXISTS, \ + pool, 0, 0, 0, 0); \ + _qzz_res; \ + }) + +/* Mark a piece of memory as being a stack. Returns a stack id. */ +#define VALGRIND_STACK_REGISTER(start, end) \ + __extension__ \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_REGISTER, \ + start, end, 0, 0, 0); \ + _qzz_res; \ + }) + +/* Unmark the piece of memory associated with a stack id as being a + stack. */ +#define VALGRIND_STACK_DEREGISTER(id) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_DEREGISTER, \ + id, 0, 0, 0, 0); \ + } + +/* Change the start and end address of the stack id. */ +#define VALGRIND_STACK_CHANGE(id, start, end) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_CHANGE, \ + id, start, end, 0, 0); \ + } + +/* Load PDB debug info for Wine PE image_map. */ +#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__LOAD_PDB_DEBUGINFO, \ + fd, ptr, total_size, delta, 0); \ + } + + +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64_linux +#undef PLAT_arm_linux +#undef PLAT_ppc32_aix5 +#undef PLAT_ppc64_aix5 + +#endif /* __VALGRIND_H */ diff --git a/packages/CLPBN/learning/em.yap b/packages/CLPBN/learning/em.yap index 7e3f64c31..d87631a99 100644 --- a/packages/CLPBN/learning/em.yap +++ b/packages/CLPBN/learning/em.yap @@ -11,6 +11,7 @@ [clpbn_init_graph/1, clpbn_init_solver/5, clpbn_run_solver/4, + clpbn_finalize_solver/4, clpbn_flag/2]). :- use_module(library('clpbn/dists'), @@ -53,6 +54,7 @@ em(Items, MaxError, MaxIts, Tables, Likelihood) :- catch(init_em(Items, State),Error,handle_em(Error)), em_loop(0, 0.0, State, MaxError, MaxIts, Likelihood, Tables), + clpbn_finalize_solver(State), assert(em_found(Tables, Likelihood)), fail. % get rid of new random variables the easy way :)