The best I could up with is to use a fast out on the looping logic.  Combined with the possibility of using the Non-Zero approach as described  by themis, you can answer you question by inspecting less than 2% of the N^2 problem.
Below is some code that gives the timing for numbers that are between 80% and 99% zero.
When the numbers get around 88% zero, using themis' approach switches to being better (was not coded in the sample below, though).
This is not a highly theoretical solution, but it is practical.  
OK, here is some "theory" of the problem space:
Basically, each bit for X (the output) is the OR summation of the bits on the diagonal of a grid constructed by having the bits of A along the top (MSB to LSB left to right) and the bits of B along the side (MSB to LSB from top to bottom).  Since the bit of X is 1 if any on the diagonal is 1, you can perform an early out on the cell traversal. 
The code below does this and shows that even for numbers that are ~87% zero, you only have to check ~2% of the cells.  For more dense (more 1's) numbers, that percentage drops even more.  
In other words, I would not worry about tricky algorithms and just do some efficient logic checking.  I think the trick is to look at the bits of your output as the diagonals of the grid as opposed to the bits of A shift-OR with the bits of B.  The trickiest thing is this case is keeping track of the bits you can look at in A and B and how to index the bits properly. 
Hopefully this makes sense. Let me know if I need to explain this a bit further (or if you find any problems with this approach).
NOTE: If we knew your problem space a bit better, we could optimize the algorithm accordingly.  If your numbers are mostly non-zero, then this approach is better than themis since his would result is more computations and storage space needed (sizeof(int) * NNZ).
NOTE 2: This assumes the data is basically bits, and I am using .NET's BitArray to store and access the data.  I don't think this would cause any major headaches when translated to other languages. The basic idea still applies.
using System;
using System.Collections;
namespace BigIntegerOr
{
    class Program
    {
        private static Random r = new Random();
        private static BitArray WeightedToZeroes(int size, double pctZero, out int nnz)
        {
            nnz = 0;
            BitArray ba = new BitArray(size);
            for (int i = 0; i < size; i++)
            {
                ba[i] = (r.NextDouble() < pctZero) ? false : true;
                if (ba[i]) nnz++;
            }
            return ba;
        }
        static void Main(string[] args)
        {
            // make sure there are enough bytes to hold the 6000 bits
            int size = (6000 + 7) / 8;
            int bits = size * 8;
            Console.WriteLine("PCT ZERO\tSECONDS\t\tPCT CELLS\tTOTAL CELLS\tNNZ APPROACH");
            for (double pctZero = 0.8; pctZero < 1.0; pctZero += 0.01)
            {
                // fill the "BigInts"
                int nnzA, nnzB;
                BitArray a = WeightedToZeroes(bits, pctZero, out nnzA);
                BitArray b = WeightedToZeroes(bits, pctZero, out nnzB);
                // this is the answer "BigInt" that is at most twice the size minus 1
                int xSize = bits * 2 - 1;
                BitArray x = new BitArray(xSize);
                int LSB, MSB;
                LSB = MSB = bits - 1;
                // stats
                long cells = 0;
                DateTime start = DateTime.Now;
                for (int i = 0; i < xSize; i++)
                {
                    // compare using the diagonals
                    for (int bit = LSB; bit < MSB; bit++)
                    {
                        cells++;
                        x[i] |= (b[MSB - bit] && a[bit]);
                        if (x[i]) break;
                    }
                    // update the window over the bits
                    if (LSB == 0)
                    {
                        MSB--;
                    }
                    else
                    {
                        LSB--;
                    }
                    //Console.Write(".");
                }
                // stats
                TimeSpan elapsed = DateTime.Now.Subtract(start);
                double pctCells = (cells * 100.0) / (bits * bits);
                Console.WriteLine(pctZero.ToString("p") + "\t\t" +elapsed.TotalSeconds.ToString("00.000") + "\t\t" +
                    pctCells.ToString("00.00") + "\t\t" + cells.ToString("00000000") + "\t" + (nnzA * nnzB).ToString("00000000"));
            }
            Console.ReadLine();
        }
    }
}