Patchwork [v2,1/6] rs6000: Introducing define_dot_insn

login
register
mail settings
Submitter Segher Boessenkool
Date June 3, 2013, 4:26 p.m.
Message ID <899934b3dcdb3db63d8dff9773ff4befcfebf858.1370252416.git.segher@kernel.crashing.org>
Download mbox | patch
Permalink /patch/248333/
State New
Headers show

Comments

Segher Boessenkool - June 3, 2013, 4:26 p.m.
This adds a program (mdm.pl) that does some processing on machine
description files.  It is careful to keep formatting and comments
intact as much as possible, so that its output is well readable.

The only transform it does so far is convert a "define_dot_insn"
construct to three "define_insn"s and two "define_split"s, that
together handle a PowerPC GPR instruction and its record-form
("dot") variant.  The syntax of define_dot_insn is just like that
of define_insn, with an extra condition string added after the
normal one; the condition used for the record form variant is the
conjunction of both those condition strings.

The mdm.pl program is only used if the input file (*.mdm) has been
touched, so users do not need Perl installed to build GCC unless
they have changed the input file.  The output file (*.md) is checked
into SVN.

This needs Perl 5.14, which is two years old.  If that won't do
I can rework some of it so it needs only 5.10, which is five years
old.

As an example this patch moves lshrdi3 over.  This causes a minor
regression that further patches will fix: no longer a different
instruction type attribute is used for the dot form; instead, the
"dot" attribute is set to "yes".  Hopefully, with many fewer (and
more descriptive) possible values for "type" the scheduling
descriptions will be clearer and contain fewer silly bugs.

Bootstrapped and tested on powerpc64-linux --enable-languages=c,c++,fortran
--disable-libsanitizer, -m64,-m32,-m32/-mpowerpc64, no regressions.

Comments, questions, flames?  I'm still wearing my asbestos suit...


Segher


2013-06-03  Segher Boessenkool  <segher@kernel.crashing.org>

gcc/
	* config/rs6000/rs6000.md (dot): New.
	(include "integer.md"): New.
	(lshrdi3_internal1, lshrdi3_internal2, lshrdi3_internal3): Delete.
	* config/rs6000/integer.mdm: New file.
	* config/rs6000/integer.md: New file, autogenerated.
	* config/rs6000/mdm.pl: New file.
	* config/rs6000/t-rs6000 (MD_INCLUDES): Add integer.md.
	(MDM): New.
	(%.md: %.mdm): New.

contrib/
	* gcc_update (files_and_dependencies): Add rs6000 integer.md.

---
 contrib/gcc_update            |   1 +
 gcc/config/rs6000/integer.md  | 145 +++++++++++++
 gcc/config/rs6000/integer.mdm |  66 ++++++
 gcc/config/rs6000/mdm.pl      | 470 ++++++++++++++++++++++++++++++++++++++++++
 gcc/config/rs6000/rs6000.md   |  82 +-------
 gcc/config/rs6000/t-rs6000    |   6 +
 6 files changed, 693 insertions(+), 77 deletions(-)
 create mode 100644 gcc/config/rs6000/integer.md
 create mode 100644 gcc/config/rs6000/integer.mdm
 create mode 100755 gcc/config/rs6000/mdm.pl

Patch

diff --git a/contrib/gcc_update b/contrib/gcc_update
index 10a5970..5fd2f2e 100755
--- a/contrib/gcc_update
+++ b/contrib/gcc_update
@@ -89,6 +89,7 @@  gcc/config/c6x/c6x-mult.md: gcc/config/c6x/c6x-mult.md.in gcc/config/c6x/genmult
 gcc/config/m68k/m68k-tables.opt: gcc/config/m68k/m68k-devices.def gcc/config/m68k/m68k-isas.def gcc/config/m68k/m68k-microarchs.def gcc/config/m68k/genopt.sh
 gcc/config/mips/mips-tables.opt: gcc/config/mips/mips-cpus.def gcc/config/mips/genopt.sh
 gcc/config/rs6000/rs6000-tables.opt: gcc/config/rs6000/rs6000-cpus.def gcc/config/rs6000/genopt.sh
+gcc/config/rs6000/integer.md: gcc/config/rs6000/integer.mdm gcc/config/rs6000/mdm.pl
 gcc/config/tilegx/mul-tables.c: gcc/config/tilepro/gen-mul-tables.cc
 gcc/config/tilepro/mul-tables.c: gcc/config/tilepro/gen-mul-tables.cc
 # And then, language-specific files
diff --git a/gcc/config/rs6000/integer.md b/gcc/config/rs6000/integer.md
new file mode 100644
index 0000000..780e418
--- /dev/null
+++ b/gcc/config/rs6000/integer.md
@@ -0,0 +1,145 @@ 
+; Generated by mdm.pl; do not edit (edit the .mdm instead).
+; vi:ro
+
+
+; Copyright (C) 1990-2013 Free Software Foundation, Inc.
+;
+; This file is part of GCC.
+;
+; GCC is free software; you can redistribute it and/or modify it
+; under the terms of the GNU General Public License as published
+; by the Free Software Foundation; either version 3, or (at your
+; option) any later version.
+;
+; GCC is distributed in the hope that it will be useful, but WITHOUT
+; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+; License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with GCC; see the file COPYING3.  If not see
+; <http://www.gnu.org/licenses/>.
+
+
+; This file describes the integer (GPR-to-GPR) PowerPC instructions.
+; Most of these have record-form ("dot") variants, and are described
+; using define_dot_insn.
+
+
+; -- Rotate and shift instructions:
+; rlwinm[.], rlwnm[.], rldicl[.], rldicr[.], rldic[.], rldcl[.], rldcr[.]
+; rlwimi[.], rldimi[.]
+; slw[.], srw[.], srawi[.], sraw[.], sld[.], srd[.], sradi[.], srad[.]
+
+
+(define_insn "lshrdi3"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=r,r")
+	(lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "r,r")
+		     (match_operand:SI 2 "reg_or_cint_operand" "r,i")))]
+  "TARGET_POWERPC64"
+  "@
+   srd %0,%1,%2
+   srdi %0,%1,%H2"
+  [(set_attr "type" "var_shift_rotate,shift")])
+
+(define_insn "*lshrdi3_dot"
+  [(set (match_operand:CC 3 "cc_reg_operand" "=x,x,?y,?y")
+     (compare:CC
+	(lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "r,r,r,r")
+		     (match_operand:SI 2 "reg_or_cint_operand" "r,i,r,i"))
+	(const_int 0)))
+   (clobber (match_scratch:DI 0 "=r,r,r,r"))]
+  "(TARGET_POWERPC64)
+   && (DImode == Pmode && rs6000_gen_cell_microcode)"
+  "@
+   srd. %0,%1,%2
+   srdi. %0,%1,%H2
+   #
+   #"
+  [(set_attr "length" "4,4,8,8")
+   (set_attr "dot" "yes,yes,no,no")
+   (set_attr "type" "var_shift_rotate,shift,var_shift_rotate,shift")])
+
+(define_split
+  [(set (match_operand:CC 3 "cc_reg_not_cr0_operand" "")
+     (compare:CC
+	(lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "")
+		     (match_operand:SI 2 "reg_or_cint_operand" ""))
+	(const_int 0)))
+   (clobber (match_scratch:DI 0 ""))]
+  "((TARGET_POWERPC64)
+   && (DImode == Pmode && rs6000_gen_cell_microcode))
+   && (reload_completed)"
+  [(set (match_dup 0)
+	(lshiftrt:DI (match_dup 1)
+		     (match_dup 2)))
+   (set (match_dup 3)
+	(compare:CC (match_dup 0)
+		    (const_int 0)))]
+  "")
+
+(define_insn "*lshrdi3_dot2"
+  [(set (match_operand:CC 3 "cc_reg_operand" "=x,x,?y,?y")
+     (compare:CC
+	(lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "r,r,r,r")
+		     (match_operand:SI 2 "reg_or_cint_operand" "r,i,r,i"))
+	(const_int 0)))
+   (set (match_operand:DI 0 "gpc_reg_operand" "=r,r,r,r")
+	(lshiftrt:DI (match_dup 1)
+		     (match_dup 2)))]
+  "(TARGET_POWERPC64)
+   && (DImode == Pmode && rs6000_gen_cell_microcode)"
+  "@
+   srd. %0,%1,%2
+   srdi. %0,%1,%H2
+   #
+   #"
+  [(set_attr "length" "4,4,8,8")
+   (set_attr "dot" "yes,yes,no,no")
+   (set_attr "type" "var_shift_rotate,shift,var_shift_rotate,shift")])
+
+(define_split
+  [(set (match_operand:CC 3 "cc_reg_not_cr0_operand" "")
+     (compare:CC
+	(lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "")
+		     (match_operand:SI 2 "reg_or_cint_operand" ""))
+	(const_int 0)))
+   (set (match_operand:DI 0 "gpc_reg_operand" "")
+	(lshiftrt:DI (match_dup 1)
+		     (match_dup 2)))]
+  "((TARGET_POWERPC64)
+   && (DImode == Pmode && rs6000_gen_cell_microcode))
+   && (reload_completed)"
+  [(set (match_dup 0)
+	(lshiftrt:DI (match_dup 1)
+		     (match_dup 2)))
+   (set (match_dup 3)
+	(compare:CC (match_dup 0)
+		    (const_int 0)))]
+  "")
+
+
+; -- Logical instructions:
+; andi., andis., ori, oris, xori, xoris
+; and[.], or[.], xor[.], nand[.], nor[.], eqv[.], andc[.], orc[.]
+; extsb[.], extsh[.], extsw[.], cntlzw[.], cntlzd[.]
+
+
+; -- Arithmetic instructions:
+; addi, addis, add[.], subf[.], neg[.]
+; addic[.], subfic, addc[.], subfc[.]
+; adde[.], subfe[.], addme[.], subfme[.], addze[.], subfze[.]
+; mulli, mullw[.], mulhw[.], mulhwu[.], mulld[.], mulhd[.], mulhdu[.]
+; divw[.], divwu[.], divd[.], divdu[.]
+
+
+; -- Compare instructions:
+; cmpwi, cmpdi, cmpw, cmpd, cmplwi, cmpldi, cmplw, cmpld
+
+
+; -- Trap instructions:
+; twi, tw, tdi, td
+
+
+; -- System register instructions:
+; mtcrf, mfcr, mtocrf, mfocrf
diff --git a/gcc/config/rs6000/integer.mdm b/gcc/config/rs6000/integer.mdm
new file mode 100644
index 0000000..2daa86f
--- /dev/null
+++ b/gcc/config/rs6000/integer.mdm
@@ -0,0 +1,66 @@ 
+; Copyright (C) 1990-2013 Free Software Foundation, Inc.
+;
+; This file is part of GCC.
+;
+; GCC is free software; you can redistribute it and/or modify it
+; under the terms of the GNU General Public License as published
+; by the Free Software Foundation; either version 3, or (at your
+; option) any later version.
+;
+; GCC is distributed in the hope that it will be useful, but WITHOUT
+; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+; License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with GCC; see the file COPYING3.  If not see
+; <http://www.gnu.org/licenses/>.
+
+
+; This file describes the integer (GPR-to-GPR) PowerPC instructions.
+; Most of these have record-form ("dot") variants, and are described
+; using define_dot_insn.
+
+
+; -- Rotate and shift instructions:
+; rlwinm[.], rlwnm[.], rldicl[.], rldicr[.], rldic[.], rldcl[.], rldcr[.]
+; rlwimi[.], rldimi[.]
+; slw[.], srw[.], srawi[.], sraw[.], sld[.], srd[.], sradi[.], srad[.]
+
+
+(define_dot_insn "lshrdi3"
+  [(set (match_operand:DI 0 "gpc_reg_operand" "=r,r")
+	(lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "r,r")
+		     (match_operand:SI 2 "reg_or_cint_operand" "r,i")))]
+  "TARGET_POWERPC64"
+  "DImode == Pmode && rs6000_gen_cell_microcode"
+  "@
+   srd %0,%1,%2
+   srdi %0,%1,%H2"
+  [(set_attr "type" "var_shift_rotate,shift")])
+
+
+; -- Logical instructions:
+; andi., andis., ori, oris, xori, xoris
+; and[.], or[.], xor[.], nand[.], nor[.], eqv[.], andc[.], orc[.]
+; extsb[.], extsh[.], extsw[.], cntlzw[.], cntlzd[.]
+
+
+; -- Arithmetic instructions:
+; addi, addis, add[.], subf[.], neg[.]
+; addic[.], subfic, addc[.], subfc[.]
+; adde[.], subfe[.], addme[.], subfme[.], addze[.], subfze[.]
+; mulli, mullw[.], mulhw[.], mulhwu[.], mulld[.], mulhd[.], mulhdu[.]
+; divw[.], divwu[.], divd[.], divdu[.]
+
+
+; -- Compare instructions:
+; cmpwi, cmpdi, cmpw, cmpd, cmplwi, cmpldi, cmplw, cmpld
+
+
+; -- Trap instructions:
+; twi, tw, tdi, td
+
+
+; -- System register instructions:
+; mtcrf, mfcr, mtocrf, mfocrf
diff --git a/gcc/config/rs6000/mdm.pl b/gcc/config/rs6000/mdm.pl
new file mode 100755
index 0000000..607e9cc
--- /dev/null
+++ b/gcc/config/rs6000/mdm.pl
@@ -0,0 +1,470 @@ 
+#!/usr/bin/perl
+
+# Copyright (C) 2013 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published
+# by the Free Software Foundation; either version 3, or (at your
+# option) any later version.
+#
+# GCC is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+# License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+
+# Pre-process a machine description file as text.  Preserve whitespace and
+# comments as much as possible.
+#
+# Currently this just expands "define_dot_insn" constructs into three
+# "define_insn"s and two "define_split"s, which handle integer PowerPC
+# record form instructions.
+
+
+use v5.14;     # Needed for /r; otherwise, v5.10 for (?-1).
+use strict;
+use warnings;
+use re "/s";   # Newlines are not usually significant in MD files.
+
+
+# Interpolate arrays into strings without spaces between the elements.
+$" = "";
+
+
+my $ws = qr/
+		(?>
+			\s		# whitespace char
+			| ;.*?\n	# lisp comment
+			| \/\*.*?\*\/	# C comment
+		)+
+	/x;
+
+my $string = qr/
+		(?>
+			"
+			(?: \\. | [^\"] )*
+			"
+		)
+	/x;
+
+my $block = qr/
+		((?>
+			\{
+			(?: [^{}]++ | (?-1) )*+
+			\}
+		))
+	/x;
+
+# An "atom" is any RTL construct, followed by optional whitespace.
+my $atom = qr/
+		((?>
+			\( $ws?+ (?: (?-1) )++ \)
+			| \[ $ws?+ (?: (?-1) )++ \]
+			| [-:<>\w]++
+			| $string
+			| $block
+		)$ws?+)
+	/x;
+
+
+# Split an atom into something that matches $head, a list of atoms, and
+# finally something that matches $tail.  No error checking is done or
+# needed, that's the callers' job.
+sub decompose {
+	my ($x, $head, $tail) = @_;
+	$x =~ /^(\Q$head\E$ws?+)/g;
+	$head = $1;
+
+	my @body;
+	my $last = pos $x;
+	while ($x =~ /\G($atom)/g) {
+		push @body, $1;
+		$last = pos $x;
+	}
+	pos $x = $last;
+
+	$x =~ /\G(\Q$tail\E$ws?+)/g;
+	$tail = $1;
+
+	return ($head, @body, $tail);
+}
+
+# Merge two conditions (double-quoted pieces of C code) into one.
+sub join_cond {
+	my ($c1, $c2) = @_;
+
+	return $c1 if $c2 =~ /^""/;
+	return $c2 if $c1 =~ /^""/;
+
+	$c1 =~ s/^"([^"]*).*/"\($1\)\n  /;
+	$c2 =~ s/^"([^"]*)"/ && \($1\)"/;
+
+	return "$c1$c2";
+}
+
+# Double a constraint, e.g. "=r,i" becomes "=r,i,r,i".
+sub double_constraint {
+	my $x = shift;
+	return $x =~ s/(\w[^"]*)/$1,$1/r;
+}
+
+# Double all constraints in an atom.
+sub double_constraints {
+	my $pattern = shift;
+
+	if ($pattern =~ /^\(match_operand\b/) {
+		my ($head, @body) = decompose $pattern, "(", ")";
+		$body[3] = double_constraint $body[3];
+		return "$head@body";
+	}
+
+	if ($pattern =~ /^\(match_scratch\b/) {
+		my ($head, @body) = decompose $pattern, "(", ")";
+		$body[2] = double_constraint $body[2];
+		return "$head@body";
+	}
+
+	if ($pattern =~ /^[([]/) {
+		my $first = substr $pattern, 0, 1;
+		my $last = ($first eq "(") ? ")" : "]";
+		my ($head, @body) = decompose $pattern, $first, $last;
+		my $tail = pop @body;
+
+		@body = map { double_constraints($_) } @body;
+
+		return "$head@body$tail";
+	}
+
+	return $pattern;
+}
+
+# Remove a constraint, e.g. "=r,i" becomes "".
+sub remove_constraint {
+	my $x = shift;
+	return $x =~ s/"[^"]*/"/r;
+}
+
+# Remove all constraints in an atom.
+sub remove_constraints {
+	my $pattern = shift;
+
+	if ($pattern =~ /^\(match_operand\b/) {
+		my ($head, @body) = decompose $pattern, "(", ")";
+		$body[3] = remove_constraint $body[3];
+		return "$head@body";
+	}
+
+	if ($pattern =~ /^\(match_scratch\b/) {
+		my ($head, @body) = decompose $pattern, "(", ")";
+		$body[2] = remove_constraint $body[2];
+		return "$head@body";
+	}
+
+	if ($pattern =~ /^[([]/) {
+		my $first = substr $pattern, 0, 1;
+		my $last = ($first eq "(") ? ")" : "]";
+		my ($head, @body) = decompose $pattern, $first, $last;
+		my $tail = pop @body;
+
+		@body = map { remove_constraints($_) } @body;
+
+		return "$head@body$tail";
+	}
+
+	return $pattern;
+}
+
+# Change all "match_operand" and "match_scratch" to the equally numbered
+# "match_dup" construct.
+sub make_match_dups {
+	my $pattern = shift;
+
+	if ($pattern =~ /^\(match_(?:operand|scratch)\b/) {
+		my ($head, @body) = decompose $pattern, "(", ")";
+		my $tail = pop @body;
+		$body[1] =~ /(\d+)/;
+		return "${head}match_dup $1$tail";
+	}
+
+	if ($pattern =~ /^[([]/) {
+		my $first = substr $pattern, 0, 1;
+		my $last = ($first eq "(") ? ")" : "]";
+		my ($head, @body) = decompose $pattern, $first, $last;
+		my $tail = pop @body;
+
+		@body = map { make_match_dups($_) } @body;
+
+		return "$head@body$tail";
+	}
+
+	return $pattern;
+}
+
+# Find out the highest operand number used in a "match_<anything>".
+sub max_op {
+	my $pattern = shift;
+
+	if ($pattern =~ /^\(match_/) {
+		my ($head, @body) = decompose $pattern, "(", ")";
+		return $body[1];
+	}
+
+	if ($pattern =~ /^[([]/) {
+		my $first = substr $pattern, 0, 1;
+		my $last = ($first eq "(") ? ")" : "]";
+		my ($head, @body) = decompose $pattern, $first, $last;
+		pop @body;
+
+		my $max = -1;
+		for (@body) {
+			my $x = max_op($_);
+			$max = $x if $x > $max;
+		}
+		return $max;
+	}
+
+	return -1;
+}
+
+# Find out the number of alternatives in this insn pattern.
+sub num_alts {
+	my $pattern = shift;
+
+	if ($pattern =~ /^\(match_operand/) {
+		my ($head, @body) = decompose $pattern, "(", ")";
+		$body[3] =~ /"([^"]*)"/;
+		return 1 + $1 =~ tr/,//;
+	}
+
+	if ($pattern =~ /^[([]/) {
+		my $first = substr $pattern, 0, 1;
+		my $last = ($first eq "(") ? ")" : "]";
+		my ($head, @body) = decompose $pattern, $first, $last;
+		pop @body;
+
+		my $max = 1;
+		for (@body) {
+			my $x = num_alts($_);
+			$max = $x if $x > $max;
+		}
+		return $max;
+	}
+
+	return 1;
+}
+
+# Make the various new patterns used in the two "define_insn"s and the
+# two "define_split"s we manufacture: a "dot" pattern for the "compare
+# the result of the insn to zero, and clobber" version; a "dot2" pattern
+# for the "compare and set" version; and a "split" pattern for the result
+# of the splitters.
+#
+# If the original pattern is a parallel, the set of the GPR that the dot
+# compare uses has to be first.
+sub make_dotpatterns {
+	my ($pattern, $n_ops, $n_alts) = @_;
+
+	my ($head, @body) = decompose $pattern, "[", "]";
+	my $tail = pop @body;
+
+	my $first = shift @body;
+	my ($ihead, @ibody) = decompose $first, "(", ")";
+	my $itail = pop @ibody;
+	die "$ARGV: insn not a valid SET in define_dot_insn pattern: $pattern\n"
+		if $ibody[0] !~ /^set\b/ or scalar @ibody != 3;
+	my ($set, $dst, $src) = @ibody;
+
+	my $compare = "(compare:CC\n\t$src\n\t(const_int 0))";
+	$compare = double_constraints $compare;
+	my $constraint = (",x" x $n_alts) . (",?y" x $n_alts);
+	$constraint =~ s/^,/=/;
+
+	my $set_cc = "$set(match_operand:CC $n_ops " .
+		qq/"cc_reg_operand" "$constraint")\n     /;
+
+	my @double_body = map { double_constraints $_ } @body;
+
+	my $dotset = "(clobber $dst" =~ s/\).*/))/r =~ s/operand/scratch/r;
+	$dotset =~ s/"[^"]*"$ws//;
+	$dotset = double_constraints $dotset;
+	my $dotpat = "$head$ihead$set_cc$compare)";
+	$dotpat .= "\n   @double_body" if scalar @body;
+	$dotpat .= "\n   $dotset$tail";
+
+	my $src_dup = make_match_dups $src;
+	my $dot2set = double_constraints "$ihead$set$dst$src_dup$itail";
+	my $dot2pat = "$head$ihead$set_cc$compare)\n   $dot2set";
+	$dot2pat .= "@double_body" if scalar @body;
+	$dot2pat .= $tail;
+
+	my @dup_body = map { make_match_dups $_ } @body;
+
+	my $dup = make_match_dups $first;
+	my @dd = decompose $dup, "(", ")";
+	$dd[2] =~ s/\).*/)/;
+	my $comp = "(set (match_dup $n_ops)\n\t" .
+		"(compare:CC $dd[2]\n\t\t    " .
+		"(const_int 0)))";
+	my $splitpat = "$dup";
+	$splitpat = "(parallel [\n   $splitpat@dup_body])" if (scalar @body);
+	$splitpat = "$head$splitpat\n   $comp$tail";
+
+	return ($dotpat, $dot2pat, $splitpat);
+}
+
+# Construct an output template suitable for the dot instructions: change
+# the mnemonics to include the ".", and add "#" alternatives for the split.
+sub make_dottemplate {
+	my ($template, $n_alts) = @_;
+
+	die "$ARGV: template not a string: $template\n" if $template !~ /^"/;
+
+	$template =~ s/^"(?:\s*@\s*)?//;
+	$template =~ s/(^\s*+\S++)/$1./mg;
+	my $hashes = "\n   #" x $n_alts;
+	$template =~ s/"/$hashes"/;
+	return qq/"@\n   $template/;
+}
+
+# Change a set_attr with multiple alternatives to have those alternatives
+# twice, that is, for the split versions as well.
+sub make_dotattr {
+	my $attr = shift;
+
+	die "$ARGV: syntax error in attr: $attr\n" if $attr !~ /^\(/;
+
+	my ($head, @body) = decompose $attr, "(", ")";
+
+	$body[2] =~ s/"([^"]*)"/"$1,$1"/
+		if $body[0] =~ /^set_attr\b/
+		and $body[2] =~ /,/;
+
+	return "$head@body";
+}
+
+# Make an attribute vector for a dot insn: double all existing attributes,
+# add a "length" attribute, add a "dot" attribute.
+#
+# This currently only supports single insns in the pattern.
+sub make_dotattrs {
+	my ($attrs, $n_alts) = @_;
+
+	$attrs ||= "[]";
+
+	my ($head, @body) = decompose $attrs, "[", "]";
+	my $tail = pop @body;
+
+	@body = map { make_dotattr($_) } @body;
+
+	my $length_attr = (",4" x $n_alts) . (",8" x $n_alts);
+	$length_attr =~ s/^,//;
+	$length_attr = qq/(set_attr "length" "$length_attr")\n   /;
+
+	my $dot_attr = (",yes" x $n_alts) . (",no" x $n_alts);
+	$dot_attr =~ s/^,//;
+	$dot_attr = qq/(set_attr "dot" "$dot_attr")/;
+	$dot_attr .= "\n   " unless $attrs eq "[]";
+
+	return "$head$length_attr$dot_attr@body$tail";
+}
+
+# Split a "define_dot_insn" into the appropriate three "define_insn"s and
+# two "define_split"s.
+sub handle_define_dot_insn {
+	my $x = shift;
+	my ($head, @body) = decompose $x, "(", ")";
+	my $tail = pop @body;
+
+	# TODO: check the atom types of @body here?  This script might
+	# die a fiery death if the input file gets it wrong.
+
+	my $define_dot_insn = shift @body;
+	my $name = shift @body;
+	my $pattern = shift @body;
+	my $cond = shift @body;
+	my $dotcond = shift @body;
+	my $template = shift @body;
+	my $attrs = shift @body;
+
+	my $n_ops = 1 + max_op $pattern;
+	my $n_alts = num_alts $pattern;
+
+	my $define_insn = $define_dot_insn =~ s/dot_//r;
+	my $define_split = $name =~ s/^"[^"]*"/define_split/r;
+
+	my $name1 = $name =~ s/^"\*?([^"]*)/"*$1_dot/r;
+	my $name2 = $name =~ s/^"\*?([^"]*)/"*$1_dot2/r;
+
+	my ($dotpat, $dot2pat, $splittedpat) =
+		make_dotpatterns $pattern, $n_ops, $n_alts;
+
+	$dotcond = join_cond $cond, $dotcond;
+
+	my $dottemplate = make_dottemplate $template, $n_alts;
+
+	my $dotattrs = make_dotattrs $attrs, $n_alts;
+
+	my $splitpat = remove_constraints $dotpat;
+	my $split2pat = remove_constraints $dot2pat;
+	$splitpat =~ s/cc_reg/cc_reg_not_cr0/;
+	$split2pat =~ s/cc_reg/cc_reg_not_cr0/;
+
+	my $splitcond = join_cond $dotcond, qq/"reload_completed"\n  /;
+
+	my $prep = qq/""/;
+
+	if (not defined $attrs) {
+		$attrs = "";
+		$dottemplate .= "\n  ";
+	}
+
+	print $head;
+	print $define_insn, $name, $pattern, $cond, $template, $attrs;
+	print ")\n\n(";
+	print $define_insn, $name1, $dotpat, $dotcond, $dottemplate, $dotattrs;
+	print ")\n\n(";
+	print $define_split, $splitpat, $splitcond, $splittedpat, $prep;
+	print ")\n\n(";
+	print $define_insn, $name2, $dot2pat, $dotcond, $dottemplate, $dotattrs;
+	print ")\n\n(";
+	print $define_split, $split2pat, $splitcond, $splittedpat, $prep;
+	print $tail;
+}
+
+sub handle_other {
+	print @_;
+}
+
+
+# Slurp the whole input file.
+my $mdm;
+{
+	local $/;
+	$mdm = <>;
+}
+
+print "; Generated by mdm.pl; do not edit (edit the .mdm instead).\n";
+print "; vi:ro\n\n\n";
+
+# Handle leading whitespace.
+$mdm =~ /^($ws?+)/g;
+print $1;
+
+# Finally, handle the rest.
+while ($mdm =~ /\G($atom|(?>.))/g) {
+	my $x = $1;
+
+	$x =~ /^\((\w+)/ or die "$ARGV: bad toplevel construct:\n$x\n";
+
+	if ($1 eq "define_dot_insn") {
+		handle_define_dot_insn $x;
+	} else {
+		handle_other $x;
+	}
+}
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index 6be05a4..7adde36 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -149,6 +149,9 @@  (define_c_enum "unspecv"
 (define_attr "type" "integer,two,three,load,load_ext,load_ext_u,load_ext_ux,load_ux,load_u,store,store_ux,store_u,fpload,fpload_ux,fpload_u,fpstore,fpstore_ux,fpstore_u,vecload,vecstore,imul,imul2,imul3,lmul,idiv,ldiv,insert_word,branch,cmp,fast_compare,compare,var_delayed_compare,delayed_compare,imul_compare,lmul_compare,fpcompare,cr_logical,delayed_cr,mfcr,mfcrf,mtcr,mfjmpr,mtjmpr,fp,fpsimple,dmul,sdiv,ddiv,ssqrt,dsqrt,jmpreg,brinc,vecsimple,veccomplex,vecdiv,veccmp,veccmpsimple,vecperm,vecfloat,vecfdiv,vecdouble,isync,sync,load_l,store_c,shift,trap,insert_dword,var_shift_rotate,cntlz,exts,mffgpr,mftgpr,isel,popcnt,crypto"
   (const_string "integer"))
 
+;; Is this instruction a "dot" (record) instruction or not?
+(define_attr "dot" "no,yes" (const_string "no"))
+
 ;; Define floating point instruction sub-types for use with Xfpu.md
 (define_attr "fp_type" "fp_default,fp_addsub_s,fp_addsub_d,fp_mul_s,fp_mul_d,fp_div_s,fp_div_d,fp_maddsub_s,fp_maddsub_d,fp_sqrt_s,fp_sqrt_d" (const_string "fp_default"))
 
@@ -361,6 +364,8 @@  (define_mode_attr E500_CONVERT [(SF "!TARGET_FPRS")
 (define_mode_attr TARGET_FLOAT [(SF "TARGET_SINGLE_FLOAT")
 				(DF "TARGET_DOUBLE_FLOAT")])
 
+(include "integer.md")
+
 ;; Start with fixed-point load and store insns.  Here we put only the more
 ;; complex forms.  Basic data transfer is done later.
 
@@ -7671,83 +7676,6 @@  (define_split
 		    (const_int 0)))]
   "")
 
-(define_expand "lshrdi3"
-  [(set (match_operand:DI 0 "gpc_reg_operand" "")
-	(lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "")
-		     (match_operand:SI 2 "reg_or_cint_operand" "")))]
-  "TARGET_POWERPC64"
-  "")
-
-(define_insn "*lshrdi3_internal1"
-  [(set (match_operand:DI 0 "gpc_reg_operand" "=r,r")
-	(lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "r,r")
-		     (match_operand:SI 2 "reg_or_cint_operand" "r,i")))]
-  "TARGET_POWERPC64"
-  "@
-   srd %0,%1,%2
-   srdi %0,%1,%H2"
-  [(set_attr "type" "var_shift_rotate,shift")])
-
-(define_insn "*lshrdi3_internal2"
-  [(set (match_operand:CC 0 "cc_reg_operand" "=x,x,?y,?y")
-	(compare:CC (lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "r,r,r,r")
-				 (match_operand:SI 2 "reg_or_cint_operand" "r,i,r,i"))
-		    (const_int 0)))
-   (clobber (match_scratch:DI 3 "=r,r,r,r"))]
-  "TARGET_64BIT "
-  "@
-   srd. %3,%1,%2
-   srdi. %3,%1,%H2
-   #
-   #"
-  [(set_attr "type" "var_delayed_compare,delayed_compare,var_delayed_compare,delayed_compare")
-   (set_attr "length" "4,4,8,8")])
-
-(define_split
-  [(set (match_operand:CC 0 "cc_reg_not_micro_cr0_operand" "")
-	(compare:CC (lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "")
-				 (match_operand:SI 2 "reg_or_cint_operand" ""))
-		    (const_int 0)))
-   (clobber (match_scratch:DI 3 ""))]
-  "TARGET_POWERPC64 && reload_completed"
-  [(set (match_dup 3)
-	(lshiftrt:DI (match_dup 1) (match_dup 2)))
-   (set (match_dup 0)
-	(compare:CC (match_dup 3)
-		    (const_int 0)))]
-  "")
-
-(define_insn "*lshrdi3_internal3"
-  [(set (match_operand:CC 3 "cc_reg_operand" "=x,x,?y,?y")
-	(compare:CC (lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "r,r,r,r")
-				 (match_operand:SI 2 "reg_or_cint_operand" "r,i,r,i"))
-		    (const_int 0)))
-   (set (match_operand:DI 0 "gpc_reg_operand" "=r,r,r,r")
-	(lshiftrt:DI (match_dup 1) (match_dup 2)))]
-  "TARGET_64BIT"
-  "@
-   srd. %0,%1,%2
-   srdi. %0,%1,%H2
-   #
-   #"
-  [(set_attr "type" "var_delayed_compare,delayed_compare,var_delayed_compare,delayed_compare")
-   (set_attr "length" "4,4,8,8")])
-
-(define_split
-  [(set (match_operand:CC 3 "cc_reg_not_micro_cr0_operand" "")
-	(compare:CC (lshiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "")
-				 (match_operand:SI 2 "reg_or_cint_operand" ""))
-		    (const_int 0)))
-   (set (match_operand:DI 0 "gpc_reg_operand" "")
-	(lshiftrt:DI (match_dup 1) (match_dup 2)))]
-  "TARGET_POWERPC64 && reload_completed"
-  [(set (match_dup 0)
-	(lshiftrt:DI (match_dup 1) (match_dup 2)))
-   (set (match_dup 3)
-	(compare:CC (match_dup 0)
-		    (const_int 0)))]
-  "")
-
 (define_expand "ashrdi3"
   [(set (match_operand:DI 0 "gpc_reg_operand" "")
 	(ashiftrt:DI (match_operand:DI 1 "gpc_reg_operand" "")
diff --git a/gcc/config/rs6000/t-rs6000 b/gcc/config/rs6000/t-rs6000
index ecfdf0e..5e1cf54 100644
--- a/gcc/config/rs6000/t-rs6000
+++ b/gcc/config/rs6000/t-rs6000
@@ -65,6 +65,7 @@  MD_INCLUDES = $(srcdir)/config/rs6000/rs64.md \
 	$(srcdir)/config/rs6000/a2.md \
 	$(srcdir)/config/rs6000/predicates.md \
 	$(srcdir)/config/rs6000/constraints.md \
+	$(srcdir)/config/rs6000/integer.md \
 	$(srcdir)/config/rs6000/darwin.md \
 	$(srcdir)/config/rs6000/sync.md \
 	$(srcdir)/config/rs6000/vector.md \
@@ -74,3 +75,8 @@  MD_INCLUDES = $(srcdir)/config/rs6000/rs64.md \
 	$(srcdir)/config/rs6000/spe.md \
 	$(srcdir)/config/rs6000/dfp.md \
 	$(srcdir)/config/rs6000/paired.md
+
+MDM := $(srcdir)/config/rs6000/mdm.pl
+
+$(srcdir)/config/rs6000/%.md: $(srcdir)/config/rs6000/%.mdm $(MDM)
+	$(MDM) $< > $@ || (rm $@ ; exit 1)