Graphviz programs generally don't like intentionally placing nodes on top of other nodes, but allow it under special circumstances - neato -n and neato -n2 allow it (see the FAQ).

I added a new attribute (dotme) to this input file:
digraph G {
node [shape=circle penwidth=2 label=""]
n1 [ xlabel="P1" dotme=1]
n2 [ xlabel="P2" ]
n3 [ xlabel="P3" dotme=1]
n1 -> n2
n1 -> n3
n2 -> n3
}
then ran it through this set of Graphviz programs:
dot -Tdot needsadot.gv |gvpr -c -fdotMe.gvpr |neato -n2 -Tpng >needsadot.png
where the gvpr program dotMe.gvpr is here:
BEGIN {
int nxt=0;
}
N {
int i;
string str1, str2;
node_t n;
if (hasAttr($, "dotme")){
if (strcmp($.dotme,"1")==0){
// create a new node and position it on top of existing node
str1=sprintf("__dot_%d", ++nxt);
n=node($G, str1);
n.pos=$.pos;
n.shape="point";
}
}
}
dotMe.gvpr adds a new node on top of (same pos) every node where dotme==1.
Convoluted, but reusable.