summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--all.h2
-rw-r--r--amd64/emit.c22
-rw-r--r--amd64/isel.c2
-rw-r--r--arm64/isel.c2
-rw-r--r--gas.c78
5 files changed, 60 insertions, 46 deletions
diff --git a/all.h b/all.h
index 1abd23c..629fea3 100644
--- a/all.h
+++ b/all.h
@@ -517,5 +517,5 @@ void rega(Fn *);
 extern char *gasloc;
 extern char *gassym;
 void gasemitdat(Dat *, FILE *);
-int gasstashfp(int64_t, int);
+int gasstash(void *, int);
 void gasemitfin(FILE *);
diff --git a/amd64/emit.c b/amd64/emit.c
index 4d4d3be..51833b4 100644
--- a/amd64/emit.c
+++ b/amd64/emit.c
@@ -350,6 +350,11 @@ Next:
 	goto Next;
 }
 
+static void *negmask[4] = {
+	[Ks] = (uint32_t[4]){ 0x80000000 },
+	[Kd] = (uint64_t[2]){ 0x8000000000000000 },
+};
+
 static void
 emitins(Ins i, Fn *fn, FILE *f)
 {
@@ -398,16 +403,25 @@ emitins(Ins i, Fn *fn, FILE *f)
 		goto Table;
 	case Osub:
 		/* we have to use the negation trick to handle
-		 * some 3-address substractions */
+		 * some 3-address subtractions */
 		if (req(i.to, i.arg[1])) {
-			emitf("neg%k %=", &i, fn, f);
+			if (KBASE(i.cls) == 0)
+				emitf("neg%k %=", &i, fn, f);
+			else
+				fprintf(f,
+					"\txorp%c %sfp%d(%%rip), %%%s\n",
+					"xxsd"[i.cls],
+					gasloc,
+					gasstash(negmask[i.cls], 16),
+					regtoa(i.to.val, SLong)
+				);
 			emitf("add%k %0, %=", &i, fn, f);
 			break;
 		}
 		goto Table;
 	case Odiv:
-		/* adjust the instruction when the conversion to
-		 * a 2-address division is impossible */
+		/* use xmm15 to adjust the instruction when the
+		 * conversion to 2-address in emitf() would fail */
 		if (req(i.to, i.arg[1])) {
 			i.arg[1] = TMP(XMM0+15);
 			emitf("mov%k %=, %1", &i, fn, f);
diff --git a/amd64/isel.c b/amd64/isel.c
index 180439b..4202610 100644
--- a/amd64/isel.c
+++ b/amd64/isel.c
@@ -80,7 +80,7 @@ fixarg(Ref *r, int k, int op, Fn *fn)
 		memset(&a, 0, sizeof a);
 		a.offset.type = CAddr;
 		a.offset.local = 1;
-		n = gasstashfp(fn->con[r0.val].bits.i, KWIDE(k));
+		n = gasstash(&fn->con[r0.val].bits, KWIDE(k) ? 8 : 4);
 		sprintf(buf, "fp%d", n);
 		a.offset.label = intern(buf);
 		fn->mem[fn->nmem-1] = a;
diff --git a/arm64/isel.c b/arm64/isel.c
index 7ab368f..59f1579 100644
--- a/arm64/isel.c
+++ b/arm64/isel.c
@@ -84,7 +84,7 @@ fixarg(Ref *pr, int k, int phi, Fn *fn)
 			emit(Ocopy, k, r1, r0, R);
 		} else {
 			c = &fn->con[r0.val];
-			n = gasstashfp(c->bits.i, KWIDE(k));
+			n = gasstash(&c->bits, KWIDE(k) ? 8 : 4);
 			vgrow(&fn->con, ++fn->ncon);
 			c = &fn->con[fn->ncon-1];
 			sprintf(buf, "fp%d", n);
diff --git a/gas.c b/gas.c
index c1fd6df..6c3317d 100644
--- a/gas.c
+++ b/gas.c
@@ -54,34 +54,30 @@ gasemitdat(Dat *d, FILE *f)
 	}
 }
 
-typedef struct FBits FBits;
+typedef struct Asmbits Asmbits;
 
-struct FBits {
-	union {
-		int64_t n;
-		float f;
-		double d;
-	} bits;
-	int wide;
-	FBits *link;
+struct Asmbits {
+	char bits[16];
+	int size;
+	Asmbits *link;
 };
 
-static FBits *stash;
+static Asmbits *stash;
 
 int
-gasstashfp(int64_t n, int w)
+gasstash(void *bits, int size)
 {
-	FBits **pb, *b;
+	Asmbits **pb, *b;
 	int i;
 
-	/* does a dumb de-dup of fp constants
-	 * this should be the linker's job */
+	assert(size == 4 || size == 8 || size == 16);
 	for (pb=&stash, i=0; (b=*pb); pb=&b->link, i++)
-		if (n == b->bits.n && w == b->wide)
+		if (size <= b->size)
+		if (memcmp(bits, b->bits, size) == 0)
 			return i;
 	b = emalloc(sizeof *b);
-	b->bits.n = n;
-	b->wide = w;
+	memcpy(b->bits, bits, size);
+	b->size = size;
 	b->link = 0;
 	*pb = b;
 	return i;
@@ -90,31 +86,35 @@ gasstashfp(int64_t n, int w)
 void
 gasemitfin(FILE *f)
 {
-	FBits *b;
-	int i;
+	Asmbits *b;
+	char *p;
+	int sz, i;
+	double d;
 
 	if (!stash)
 		return;
-	fprintf(f, "/* floating point constants */\n");
-	fprintf(f, ".data\n.align 8\n");
-	for (b=stash, i=0; b; b=b->link, i++)
-		if (b->wide)
-			fprintf(f,
-				"%sfp%d:\n"
-				"\t.quad %"PRId64
-				" /* %f */\n",
-				gasloc, i, b->bits.n,
-				b->bits.d
-			);
-	for (b=stash, i=0; b; b=b->link, i++)
-		if (!b->wide)
-			fprintf(f,
-				"%sfp%d:\n"
-				"\t.long %"PRId64
-				" /* %lf */\n",
-				gasloc, i, b->bits.n & 0xffffffff,
-				b->bits.f
-			);
+	fprintf(f, "/* floating point constants */\n.data\n");
+	for (sz=16; sz>=4; sz/=2)
+		for (b=stash, i=0; b; b=b->link, i++) {
+			if (b->size == sz) {
+				fprintf(f,
+					".align %d\n"
+					"%sfp%d:",
+					sz, gasloc, i
+				);
+				for (p=b->bits; p<&b->bits[sz]; p+=4)
+					fprintf(f, "\n\t.int %"PRId32,
+						*(int32_t *)p);
+				if (sz <= 8) {
+					if (sz == 4)
+						d = *(float *)b->bits;
+					else
+						d = *(double *)b->bits;
+					fprintf(f, " /* %f */\n", d);
+				} else
+					fprintf(f, "\n");
+			}
+		}
 	while ((b=stash)) {
 		stash = b->link;
 		free(b);